Update #0 - First Release

This commit is contained in:
LAX1DUDE
2022-12-25 01:12:28 -08:00
commit e7179fad45
2154 changed files with 256324 additions and 0 deletions

View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2006 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.Closeable;
import java.io.Flushable;
import java.io.IOException;
import java.io.Writer;
import javax.annotation.Nullable;
/**
* Writer that places all output on an {@link Appendable} target. If the target
* is {@link Flushable} or {@link Closeable}, flush()es and close()s will also
* be delegated to the target.
*
* @author Alan Green
* @author Sebastian Kanthak
* @since 1.0
*/
class AppendableWriter extends Writer {
private final Appendable target;
private boolean closed;
/**
* Creates a new writer that appends everything it writes to {@code target}.
*
* @param target target to which to append output
*/
AppendableWriter(Appendable target) {
this.target = checkNotNull(target);
}
/*
* Abstract methods from Writer
*/
@Override
public void write(char cbuf[], int off, int len) throws IOException {
checkNotClosed();
// It turns out that creating a new String is usually as fast, or faster
// than wrapping cbuf in a light-weight CharSequence.
target.append(new String(cbuf, off, len));
}
@Override
public void flush() throws IOException {
checkNotClosed();
if (target instanceof Flushable) {
((Flushable) target).flush();
}
}
@Override
public void close() throws IOException {
this.closed = true;
if (target instanceof Closeable) {
((Closeable) target).close();
}
}
/*
* Override a few functions for performance reasons to avoid creating
* unnecessary strings.
*/
@Override
public void write(int c) throws IOException {
checkNotClosed();
target.append((char) c);
}
@Override
public void write(@Nullable String str) throws IOException {
checkNotClosed();
target.append(str);
}
@Override
public void write(@Nullable String str, int off, int len) throws IOException {
checkNotClosed();
// tricky: append takes start, end pair...
target.append(str, off, off + len);
}
@Override
public Writer append(char c) throws IOException {
checkNotClosed();
target.append(c);
return this;
}
@Override
public Writer append(@Nullable CharSequence charSeq) throws IOException {
checkNotClosed();
target.append(charSeq);
return this;
}
@Override
public Writer append(@Nullable CharSequence charSeq, int start, int end) throws IOException {
checkNotClosed();
target.append(charSeq, start, end);
return this;
}
private void checkNotClosed() throws IOException {
if (closed) {
throw new IOException("Cannot write to a closed writer.");
}
}
}

View File

@ -0,0 +1,925 @@
/*
* 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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkPositionIndexes;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.io.GwtWorkarounds.asCharInput;
import static com.google.common.io.GwtWorkarounds.asCharOutput;
import static com.google.common.io.GwtWorkarounds.asInputStream;
import static com.google.common.io.GwtWorkarounds.asOutputStream;
import static com.google.common.io.GwtWorkarounds.stringBuilderOutput;
import static com.google.common.math.IntMath.divide;
import static com.google.common.math.IntMath.log2;
import static java.math.RoundingMode.CEILING;
import static java.math.RoundingMode.FLOOR;
import static java.math.RoundingMode.UNNECESSARY;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.Arrays;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Ascii;
import com.google.common.base.CharMatcher;
import com.google.common.io.GwtWorkarounds.ByteInput;
import com.google.common.io.GwtWorkarounds.ByteOutput;
import com.google.common.io.GwtWorkarounds.CharInput;
import com.google.common.io.GwtWorkarounds.CharOutput;
/**
* A binary encoding scheme for reversibly translating between byte sequences
* and printable ASCII strings. This class includes several constants for
* encoding schemes specified by
* <a href="http://tools.ietf.org/html/rfc4648">RFC 4648</a>. For example, the
* expression:
*
* <pre>
* {@code
* BaseEncoding.base32().encode("foo".getBytes(Charsets.US_ASCII))}
* </pre>
*
* <p>
* returns the string {@code "MZXW6==="}, and
*
* <pre>
* {
* &#64;code
* byte[] decoded = BaseEncoding.base32().decode("MZXW6===");
* }
* </pre>
*
* <p>
* ...returns the ASCII bytes of the string {@code "foo"}.
*
* <p>
* By default, {@code BaseEncoding}'s behavior is relatively strict and in
* accordance with RFC 4648. Decoding rejects characters in the wrong case,
* though padding is optional. To modify encoding and decoding behavior, use
* configuration methods to obtain a new encoding with modified behavior:
*
* <pre>
* {@code
* BaseEncoding.base16().lowerCase().decode("deadbeef");}
* </pre>
*
* <p>
* Warning: BaseEncoding instances are immutable. Invoking a configuration
* method has no effect on the receiving instance; you must store and use the
* new encoding instance it returns, instead.
*
* <pre>
* {@code
* // Do NOT do this
* BaseEncoding hex = BaseEncoding.base16();
* hex.lowerCase(); // does nothing!
* return hex.decode("deadbeef"); // throws an IllegalArgumentException}
* </pre>
*
* <p>
* It is guaranteed that {@code encoding.decode(encoding.encode(x))} is always
* equal to {@code x}, but the reverse does not necessarily hold.
*
* <p>
* <table>
* <tr>
* <th>Encoding
* <th>Alphabet
* <th>{@code char:byte} ratio
* <th>Default padding
* <th>Comments
* <tr>
* <td>{@link #base16()}
* <td>0-9 A-F
* <td>2.00
* <td>N/A
* <td>Traditional hexadecimal. Defaults to upper case.
* <tr>
* <td>{@link #base32()}
* <td>A-Z 2-7
* <td>1.60
* <td>=
* <td>Human-readable; no possibility of mixing up 0/O or 1/I. Defaults to upper
* case.
* <tr>
* <td>{@link #base32Hex()}
* <td>0-9 A-V
* <td>1.60
* <td>=
* <td>"Numerical" base 32; extended from the traditional hex alphabet. Defaults
* to upper case.
* <tr>
* <td>{@link #base64()}
* <td>A-Z a-z 0-9 + /
* <td>1.33
* <td>=
* <td>
* <tr>
* <td>{@link #base64Url()}
* <td>A-Z a-z 0-9 - _
* <td>1.33
* <td>=
* <td>Safe to use as filenames, or to pass in URLs without escaping
* </table>
*
* <p>
* All instances of this class are immutable, so they may be stored safely as
* static constants.
*
* @author Louis Wasserman
* @since 14.0
*/
@Beta
@GwtCompatible(emulated = true)
public abstract class BaseEncoding {
// TODO(user): consider adding encodeTo(Appendable, byte[], [int, int])
BaseEncoding() {
}
/**
* Exception indicating invalid base-encoded input encountered while decoding.
*
* @author Louis Wasserman
* @since 15.0
*/
public static final class DecodingException extends IOException {
DecodingException(String message) {
super(message);
}
DecodingException(Throwable cause) {
super(cause);
}
}
/**
* Encodes the specified byte array, and returns the encoded {@code String}.
*/
public String encode(byte[] bytes) {
return encode(checkNotNull(bytes), 0, bytes.length);
}
/**
* Encodes the specified range of the specified byte array, and returns the
* encoded {@code String}.
*/
public final String encode(byte[] bytes, int off, int len) {
checkNotNull(bytes);
checkPositionIndexes(off, off + len, bytes.length);
CharOutput result = stringBuilderOutput(maxEncodedSize(len));
ByteOutput byteOutput = encodingStream(result);
try {
for (int i = 0; i < len; i++) {
byteOutput.write(bytes[off + i]);
}
byteOutput.close();
} catch (IOException impossible) {
throw new AssertionError("impossible");
}
return result.toString();
}
/**
* Returns an {@code OutputStream} that encodes bytes using this encoding into
* the specified {@code Writer}. When the returned {@code OutputStream} is
* closed, so is the backing {@code Writer}.
*/
@GwtIncompatible("Writer,OutputStream")
public final OutputStream encodingStream(Writer writer) {
return asOutputStream(encodingStream(asCharOutput(writer)));
}
/**
* Returns a {@code ByteSink} that writes base-encoded bytes to the specified
* {@code CharSink}.
*/
@GwtIncompatible("ByteSink,CharSink")
public final ByteSink encodingSink(final CharSink encodedSink) {
checkNotNull(encodedSink);
return new ByteSink() {
@Override
public OutputStream openStream() throws IOException {
return encodingStream(encodedSink.openStream());
}
};
}
// TODO(user): document the extent of leniency, probably after adding
// ignore(CharMatcher)
private static byte[] extract(byte[] result, int length) {
if (length == result.length) {
return result;
} else {
byte[] trunc = new byte[length];
System.arraycopy(result, 0, trunc, 0, length);
return trunc;
}
}
/**
* Decodes the specified character sequence, and returns the resulting
* {@code byte[]}. This is the inverse operation to {@link #encode(byte[])}.
*
* @throws IllegalArgumentException if the input is not a valid encoded string
* according to this encoding.
*/
public final byte[] decode(CharSequence chars) {
try {
return decodeChecked(chars);
} catch (DecodingException badInput) {
throw new IllegalArgumentException(badInput);
}
}
/**
* Decodes the specified character sequence, and returns the resulting
* {@code byte[]}. This is the inverse operation to {@link #encode(byte[])}.
*
* @throws DecodingException if the input is not a valid encoded string
* according to this encoding.
*/
final byte[] decodeChecked(CharSequence chars) throws DecodingException {
chars = padding().trimTrailingFrom(chars);
ByteInput decodedInput = decodingStream(asCharInput(chars));
byte[] tmp = new byte[maxDecodedSize(chars.length())];
int index = 0;
try {
for (int i = decodedInput.read(); i != -1; i = decodedInput.read()) {
tmp[index++] = (byte) i;
}
} catch (DecodingException badInput) {
throw badInput;
} catch (IOException impossible) {
throw new AssertionError(impossible);
}
return extract(tmp, index);
}
/**
* Returns an {@code InputStream} that decodes base-encoded input from the
* specified {@code Reader}. The returned stream throws a
* {@link DecodingException} upon decoding-specific errors.
*/
@GwtIncompatible("Reader,InputStream")
public final InputStream decodingStream(Reader reader) {
return asInputStream(decodingStream(asCharInput(reader)));
}
/**
* Returns a {@code ByteSource} that reads base-encoded bytes from the specified
* {@code CharSource}.
*/
@GwtIncompatible("ByteSource,CharSource")
public final ByteSource decodingSource(final CharSource encodedSource) {
checkNotNull(encodedSource);
return new ByteSource() {
@Override
public InputStream openStream() throws IOException {
return decodingStream(encodedSource.openStream());
}
};
}
// Implementations for encoding/decoding
abstract int maxEncodedSize(int bytes);
abstract ByteOutput encodingStream(CharOutput charOutput);
abstract int maxDecodedSize(int chars);
abstract ByteInput decodingStream(CharInput charInput);
abstract CharMatcher padding();
// Modified encoding generators
/**
* Returns an encoding that behaves equivalently to this encoding, but omits any
* padding characters as specified by
* <a href="http://tools.ietf.org/html/rfc4648#section-3.2">RFC 4648 section
* 3.2</a>, Padding of Encoded Data.
*/
@CheckReturnValue
public abstract BaseEncoding omitPadding();
/**
* Returns an encoding that behaves equivalently to this encoding, but uses an
* alternate character for padding.
*
* @throws IllegalArgumentException if this padding character is already used in
* the alphabet or a separator
*/
@CheckReturnValue
public abstract BaseEncoding withPadChar(char padChar);
/**
* Returns an encoding that behaves equivalently to this encoding, but adds a
* separator string after every {@code n} characters. Any occurrences of any
* characters that occur in the separator are skipped over in decoding.
*
* @throws IllegalArgumentException if any alphabet or padding characters
* appear in the separator string, or if
* {@code n <= 0}
* @throws UnsupportedOperationException if this encoding already uses a
* separator
*/
@CheckReturnValue
public abstract BaseEncoding withSeparator(String separator, int n);
/**
* Returns an encoding that behaves equivalently to this encoding, but encodes
* and decodes with uppercase letters. Padding and separator characters remain
* in their original case.
*
* @throws IllegalStateException if the alphabet used by this encoding contains
* mixed upper- and lower-case characters
*/
@CheckReturnValue
public abstract BaseEncoding upperCase();
/**
* Returns an encoding that behaves equivalently to this encoding, but encodes
* and decodes with lowercase letters. Padding and separator characters remain
* in their original case.
*
* @throws IllegalStateException if the alphabet used by this encoding contains
* mixed upper- and lower-case characters
*/
@CheckReturnValue
public abstract BaseEncoding lowerCase();
private static final BaseEncoding BASE64 = new StandardBaseEncoding("base64()",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", '=');
/**
* The "base64" base encoding specified by
* <a href="http://tools.ietf.org/html/rfc4648#section-4">RFC 4648 section
* 4</a>, Base 64 Encoding. (This is the same as the base 64 encoding from
* <a href="http://tools.ietf.org/html/rfc3548#section-3">RFC 3548</a>.)
*
* <p>
* The character {@code '='} is used for padding, but can be
* {@linkplain #omitPadding() omitted} or {@linkplain #withPadChar(char)
* replaced}.
*
* <p>
* No line feeds are added by default, as per
* <a href="http://tools.ietf.org/html/rfc4648#section-3.1"> RFC 4648 section
* 3.1</a>, Line Feeds in Encoded Data. Line feeds may be added using
* {@link #withSeparator(String, int)}.
*/
public static BaseEncoding base64() {
return BASE64;
}
private static final BaseEncoding BASE64_URL = new StandardBaseEncoding("base64Url()",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", '=');
/**
* The "base64url" encoding specified by
* <a href="http://tools.ietf.org/html/rfc4648#section-5">RFC 4648 section
* 5</a>, Base 64 Encoding with URL and Filename Safe Alphabet, also sometimes
* referred to as the "web safe Base64." (This is the same as the base 64
* encoding with URL and filename safe alphabet from
* <a href="http://tools.ietf.org/html/rfc3548#section-4">RFC 3548</a>.)
*
* <p>
* The character {@code '='} is used for padding, but can be
* {@linkplain #omitPadding() omitted} or {@linkplain #withPadChar(char)
* replaced}.
*
* <p>
* No line feeds are added by default, as per
* <a href="http://tools.ietf.org/html/rfc4648#section-3.1"> RFC 4648 section
* 3.1</a>, Line Feeds in Encoded Data. Line feeds may be added using
* {@link #withSeparator(String, int)}.
*/
public static BaseEncoding base64Url() {
return BASE64_URL;
}
private static final BaseEncoding BASE32 = new StandardBaseEncoding("base32()", "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
'=');
/**
* The "base32" encoding specified by
* <a href="http://tools.ietf.org/html/rfc4648#section-6">RFC 4648 section
* 6</a>, Base 32 Encoding. (This is the same as the base 32 encoding from
* <a href="http://tools.ietf.org/html/rfc3548#section-5">RFC 3548</a>.)
*
* <p>
* The character {@code '='} is used for padding, but can be
* {@linkplain #omitPadding() omitted} or {@linkplain #withPadChar(char)
* replaced}.
*
* <p>
* No line feeds are added by default, as per
* <a href="http://tools.ietf.org/html/rfc4648#section-3.1"> RFC 4648 section
* 3.1</a>, Line Feeds in Encoded Data. Line feeds may be added using
* {@link #withSeparator(String, int)}.
*/
public static BaseEncoding base32() {
return BASE32;
}
private static final BaseEncoding BASE32_HEX = new StandardBaseEncoding("base32Hex()",
"0123456789ABCDEFGHIJKLMNOPQRSTUV", '=');
/**
* The "base32hex" encoding specified by
* <a href="http://tools.ietf.org/html/rfc4648#section-7">RFC 4648 section
* 7</a>, Base 32 Encoding with Extended Hex Alphabet. There is no corresponding
* encoding in RFC 3548.
*
* <p>
* The character {@code '='} is used for padding, but can be
* {@linkplain #omitPadding() omitted} or {@linkplain #withPadChar(char)
* replaced}.
*
* <p>
* No line feeds are added by default, as per
* <a href="http://tools.ietf.org/html/rfc4648#section-3.1"> RFC 4648 section
* 3.1</a>, Line Feeds in Encoded Data. Line feeds may be added using
* {@link #withSeparator(String, int)}.
*/
public static BaseEncoding base32Hex() {
return BASE32_HEX;
}
private static final BaseEncoding BASE16 = new StandardBaseEncoding("base16()", "0123456789ABCDEF", null);
/**
* The "base16" encoding specified by
* <a href="http://tools.ietf.org/html/rfc4648#section-8">RFC 4648 section
* 8</a>, Base 16 Encoding. (This is the same as the base 16 encoding from
* <a href="http://tools.ietf.org/html/rfc3548#section-6">RFC 3548</a>.) This is
* commonly known as "hexadecimal" format.
*
* <p>
* No padding is necessary in base 16, so {@link #withPadChar(char)} and
* {@link #omitPadding()} have no effect.
*
* <p>
* No line feeds are added by default, as per
* <a href="http://tools.ietf.org/html/rfc4648#section-3.1"> RFC 4648 section
* 3.1</a>, Line Feeds in Encoded Data. Line feeds may be added using
* {@link #withSeparator(String, int)}.
*/
public static BaseEncoding base16() {
return BASE16;
}
private static final class Alphabet extends CharMatcher {
private final String name;
// this is meant to be immutable -- don't modify it!
private final char[] chars;
final int mask;
final int bitsPerChar;
final int charsPerChunk;
final int bytesPerChunk;
private final byte[] decodabet;
private final boolean[] validPadding;
Alphabet(String name, char[] chars) {
this.name = checkNotNull(name);
this.chars = checkNotNull(chars);
try {
this.bitsPerChar = log2(chars.length, UNNECESSARY);
} catch (ArithmeticException e) {
throw new IllegalArgumentException("Illegal alphabet length " + chars.length, e);
}
/*
* e.g. for base64, bitsPerChar == 6, charsPerChunk == 4, and bytesPerChunk ==
* 3. This makes for the smallest chunk size that still has charsPerChunk *
* bitsPerChar be a multiple of 8.
*/
int gcd = Math.min(8, Integer.lowestOneBit(bitsPerChar));
this.charsPerChunk = 8 / gcd;
this.bytesPerChunk = bitsPerChar / gcd;
this.mask = chars.length - 1;
byte[] decodabet = new byte[Ascii.MAX + 1];
Arrays.fill(decodabet, (byte) -1);
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
checkArgument(CharMatcher.ASCII.matches(c), "Non-ASCII character: %s", c);
checkArgument(decodabet[c] == -1, "Duplicate character: %s", c);
decodabet[c] = (byte) i;
}
this.decodabet = decodabet;
boolean[] validPadding = new boolean[charsPerChunk];
for (int i = 0; i < bytesPerChunk; i++) {
validPadding[divide(i * 8, bitsPerChar, CEILING)] = true;
}
this.validPadding = validPadding;
}
char encode(int bits) {
return chars[bits];
}
boolean isValidPaddingStartPosition(int index) {
return validPadding[index % charsPerChunk];
}
int decode(char ch) throws IOException {
if (ch > Ascii.MAX || decodabet[ch] == -1) {
throw new DecodingException("Unrecognized character: " + ch);
}
return decodabet[ch];
}
private boolean hasLowerCase() {
for (char c : chars) {
if (Ascii.isLowerCase(c)) {
return true;
}
}
return false;
}
private boolean hasUpperCase() {
for (char c : chars) {
if (Ascii.isUpperCase(c)) {
return true;
}
}
return false;
}
Alphabet upperCase() {
if (!hasLowerCase()) {
return this;
} else {
checkState(!hasUpperCase(), "Cannot call upperCase() on a mixed-case alphabet");
char[] upperCased = new char[chars.length];
for (int i = 0; i < chars.length; i++) {
upperCased[i] = Ascii.toUpperCase(chars[i]);
}
return new Alphabet(name + ".upperCase()", upperCased);
}
}
Alphabet lowerCase() {
if (!hasUpperCase()) {
return this;
} else {
checkState(!hasLowerCase(), "Cannot call lowerCase() on a mixed-case alphabet");
char[] lowerCased = new char[chars.length];
for (int i = 0; i < chars.length; i++) {
lowerCased[i] = Ascii.toLowerCase(chars[i]);
}
return new Alphabet(name + ".lowerCase()", lowerCased);
}
}
@Override
public boolean matches(char c) {
return CharMatcher.ASCII.matches(c) && decodabet[c] != -1;
}
@Override
public String toString() {
return name;
}
}
static final class StandardBaseEncoding extends BaseEncoding {
// TODO(user): provide a useful toString
private final Alphabet alphabet;
@Nullable
private final Character paddingChar;
StandardBaseEncoding(String name, String alphabetChars, @Nullable Character paddingChar) {
this(new Alphabet(name, alphabetChars.toCharArray()), paddingChar);
}
StandardBaseEncoding(Alphabet alphabet, @Nullable Character paddingChar) {
this.alphabet = checkNotNull(alphabet);
checkArgument(paddingChar == null || !alphabet.matches(paddingChar),
"Padding character %s was already in alphabet", paddingChar);
this.paddingChar = paddingChar;
}
@Override
CharMatcher padding() {
return (paddingChar == null) ? CharMatcher.NONE : CharMatcher.is(paddingChar.charValue());
}
@Override
int maxEncodedSize(int bytes) {
return alphabet.charsPerChunk * divide(bytes, alphabet.bytesPerChunk, CEILING);
}
@Override
ByteOutput encodingStream(final CharOutput out) {
checkNotNull(out);
return new ByteOutput() {
int bitBuffer = 0;
int bitBufferLength = 0;
int writtenChars = 0;
@Override
public void write(byte b) throws IOException {
bitBuffer <<= 8;
bitBuffer |= b & 0xFF;
bitBufferLength += 8;
while (bitBufferLength >= alphabet.bitsPerChar) {
int charIndex = (bitBuffer >> (bitBufferLength - alphabet.bitsPerChar)) & alphabet.mask;
out.write(alphabet.encode(charIndex));
writtenChars++;
bitBufferLength -= alphabet.bitsPerChar;
}
}
@Override
public void flush() throws IOException {
out.flush();
}
@Override
public void close() throws IOException {
if (bitBufferLength > 0) {
int charIndex = (bitBuffer << (alphabet.bitsPerChar - bitBufferLength)) & alphabet.mask;
out.write(alphabet.encode(charIndex));
writtenChars++;
if (paddingChar != null) {
while (writtenChars % alphabet.charsPerChunk != 0) {
out.write(paddingChar.charValue());
writtenChars++;
}
}
}
out.close();
}
};
}
@Override
int maxDecodedSize(int chars) {
return (int) ((alphabet.bitsPerChar * (long) chars + 7L) / 8L);
}
@Override
ByteInput decodingStream(final CharInput reader) {
checkNotNull(reader);
return new ByteInput() {
int bitBuffer = 0;
int bitBufferLength = 0;
int readChars = 0;
boolean hitPadding = false;
final CharMatcher paddingMatcher = padding();
@Override
public int read() throws IOException {
while (true) {
int readChar = reader.read();
if (readChar == -1) {
if (!hitPadding && !alphabet.isValidPaddingStartPosition(readChars)) {
throw new DecodingException("Invalid input length " + readChars);
}
return -1;
}
readChars++;
char ch = (char) readChar;
if (paddingMatcher.matches(ch)) {
if (!hitPadding
&& (readChars == 1 || !alphabet.isValidPaddingStartPosition(readChars - 1))) {
throw new DecodingException("Padding cannot start at index " + readChars);
}
hitPadding = true;
} else if (hitPadding) {
throw new DecodingException(
"Expected padding character but found '" + ch + "' at index " + readChars);
} else {
bitBuffer <<= alphabet.bitsPerChar;
bitBuffer |= alphabet.decode(ch);
bitBufferLength += alphabet.bitsPerChar;
if (bitBufferLength >= 8) {
bitBufferLength -= 8;
return (bitBuffer >> bitBufferLength) & 0xFF;
}
}
}
}
@Override
public void close() throws IOException {
reader.close();
}
};
}
@Override
public BaseEncoding omitPadding() {
return (paddingChar == null) ? this : new StandardBaseEncoding(alphabet, null);
}
@Override
public BaseEncoding withPadChar(char padChar) {
if (8 % alphabet.bitsPerChar == 0 || (paddingChar != null && paddingChar.charValue() == padChar)) {
return this;
} else {
return new StandardBaseEncoding(alphabet, padChar);
}
}
@Override
public BaseEncoding withSeparator(String separator, int afterEveryChars) {
checkNotNull(separator);
checkArgument(padding().or(alphabet).matchesNoneOf(separator),
"Separator cannot contain alphabet or padding characters");
return new SeparatedBaseEncoding(this, separator, afterEveryChars);
}
private transient BaseEncoding upperCase;
private transient BaseEncoding lowerCase;
@Override
public BaseEncoding upperCase() {
BaseEncoding result = upperCase;
if (result == null) {
Alphabet upper = alphabet.upperCase();
result = upperCase = (upper == alphabet) ? this : new StandardBaseEncoding(upper, paddingChar);
}
return result;
}
@Override
public BaseEncoding lowerCase() {
BaseEncoding result = lowerCase;
if (result == null) {
Alphabet lower = alphabet.lowerCase();
result = lowerCase = (lower == alphabet) ? this : new StandardBaseEncoding(lower, paddingChar);
}
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("BaseEncoding.");
builder.append(alphabet.toString());
if (8 % alphabet.bitsPerChar != 0) {
if (paddingChar == null) {
builder.append(".omitPadding()");
} else {
builder.append(".withPadChar(").append(paddingChar).append(')');
}
}
return builder.toString();
}
}
static CharInput ignoringInput(final CharInput delegate, final CharMatcher toIgnore) {
checkNotNull(delegate);
checkNotNull(toIgnore);
return new CharInput() {
@Override
public int read() throws IOException {
int readChar;
do {
readChar = delegate.read();
} while (readChar != -1 && toIgnore.matches((char) readChar));
return readChar;
}
@Override
public void close() throws IOException {
delegate.close();
}
};
}
static CharOutput separatingOutput(final CharOutput delegate, final String separator, final int afterEveryChars) {
checkNotNull(delegate);
checkNotNull(separator);
checkArgument(afterEveryChars > 0);
return new CharOutput() {
int charsUntilSeparator = afterEveryChars;
@Override
public void write(char c) throws IOException {
if (charsUntilSeparator == 0) {
for (int i = 0; i < separator.length(); i++) {
delegate.write(separator.charAt(i));
}
charsUntilSeparator = afterEveryChars;
}
delegate.write(c);
charsUntilSeparator--;
}
@Override
public void flush() throws IOException {
delegate.flush();
}
@Override
public void close() throws IOException {
delegate.close();
}
};
}
static final class SeparatedBaseEncoding extends BaseEncoding {
private final BaseEncoding delegate;
private final String separator;
private final int afterEveryChars;
private final CharMatcher separatorChars;
SeparatedBaseEncoding(BaseEncoding delegate, String separator, int afterEveryChars) {
this.delegate = checkNotNull(delegate);
this.separator = checkNotNull(separator);
this.afterEveryChars = afterEveryChars;
checkArgument(afterEveryChars > 0, "Cannot add a separator after every %s chars", afterEveryChars);
this.separatorChars = CharMatcher.anyOf(separator).precomputed();
}
@Override
CharMatcher padding() {
return delegate.padding();
}
@Override
int maxEncodedSize(int bytes) {
int unseparatedSize = delegate.maxEncodedSize(bytes);
return unseparatedSize
+ separator.length() * divide(Math.max(0, unseparatedSize - 1), afterEveryChars, FLOOR);
}
@Override
ByteOutput encodingStream(final CharOutput output) {
return delegate.encodingStream(separatingOutput(output, separator, afterEveryChars));
}
@Override
int maxDecodedSize(int chars) {
return delegate.maxDecodedSize(chars);
}
@Override
ByteInput decodingStream(final CharInput input) {
return delegate.decodingStream(ignoringInput(input, separatorChars));
}
@Override
public BaseEncoding omitPadding() {
return delegate.omitPadding().withSeparator(separator, afterEveryChars);
}
@Override
public BaseEncoding withPadChar(char padChar) {
return delegate.withPadChar(padChar).withSeparator(separator, afterEveryChars);
}
@Override
public BaseEncoding withSeparator(String separator, int afterEveryChars) {
throw new UnsupportedOperationException("Already have a separator");
}
@Override
public BaseEncoding upperCase() {
return delegate.upperCase().withSeparator(separator, afterEveryChars);
}
@Override
public BaseEncoding lowerCase() {
return delegate.lowerCase().withSeparator(separator, afterEveryChars);
}
@Override
public String toString() {
return delegate.toString() + ".withSeparator(\"" + separator + "\", " + afterEveryChars + ")";
}
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (C) 2009 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 java.io.DataInput;
import java.io.IOException;
/**
* An extension of {@code DataInput} for reading from in-memory byte arrays; its
* methods offer identical functionality but do not throw {@link IOException}.
*
* <p>
* <b>Warning:<b> The caller is responsible for not attempting to read past the
* end of the array. If any method encounters the end of the array prematurely,
* it throws {@link IllegalStateException} to signify <i>programmer error</i>.
* This behavior is a technical violation of the supertype's contract, which
* specifies a checked exception.
*
* @author Kevin Bourrillion
* @since 1.0
*/
public interface ByteArrayDataInput extends DataInput {
@Override
void readFully(byte b[]);
@Override
void readFully(byte b[], int off, int len);
@Override
int skipBytes(int n);
@Override
boolean readBoolean();
@Override
byte readByte();
@Override
int readUnsignedByte();
@Override
short readShort();
@Override
int readUnsignedShort();
@Override
char readChar();
@Override
int readInt();
@Override
long readLong();
@Override
float readFloat();
@Override
double readDouble();
@Override
String readLine();
@Override
String readUTF();
}

View File

@ -0,0 +1,83 @@
/*
* Copyright (C) 2009 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 java.io.DataOutput;
import java.io.IOException;
/**
* An extension of {@code DataOutput} for writing to in-memory byte arrays; its
* methods offer identical functionality but do not throw {@link IOException}.
*
* @author Jayaprabhakar Kadarkarai
* @since 1.0
*/
public interface ByteArrayDataOutput extends DataOutput {
@Override
void write(int b);
@Override
void write(byte b[]);
@Override
void write(byte b[], int off, int len);
@Override
void writeBoolean(boolean v);
@Override
void writeByte(int v);
@Override
void writeShort(int v);
@Override
void writeChar(int v);
@Override
void writeInt(int v);
@Override
void writeLong(long v);
@Override
void writeFloat(float v);
@Override
void writeDouble(double v);
@Override
void writeChars(String s);
@Override
void writeUTF(String s);
/**
* @deprecated This method is dangerous as it discards the high byte of every
* character. For UTF-8, use
* {@code write(s.getBytes(Charsets.UTF_8))}.
*/
@Deprecated
@Override
void writeBytes(String s);
/**
* Returns the contents that have been written to this instance, as a byte
* array.
*/
byte[] toByteArray();
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2009 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 java.io.IOException;
import com.google.common.annotations.Beta;
/**
* A callback interface to process bytes from a stream.
*
* <p>
* {@link #processBytes} will be called for each line that is read, and should
* return {@code false} when you want to stop processing.
*
* @author Chris Nokleberg
* @since 1.0
*/
@Beta
public interface ByteProcessor<T> {
/**
* This method will be called for each chunk of bytes in an input stream. The
* implementation should process the bytes from {@code buf[off]} through
* {@code buf[off + len - 1]} (inclusive).
*
* @param buf the byte array containing the data to process
* @param off the initial offset into the array
* @param len the length of data to be processed
* @return true to continue processing, false to stop
*/
boolean processBytes(byte[] buf, int off, int len) throws IOException;
/** Return the result of processing all the bytes. */
T getResult();
}

View File

@ -0,0 +1,180 @@
/*
* 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.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
/**
* A destination to which bytes can be written, such as a file. Unlike an
* {@link OutputStream}, a {@code ByteSink} is not an open, stateful stream that
* can be written to and closed. Instead, it is an immutable <i>supplier</i> of
* {@code OutputStream} instances.
*
* <p>
* {@code ByteSink} provides two kinds of methods:
* <ul>
* <li><b>Methods that return a stream:</b> These methods should return a
* <i>new</i>, independent instance each time they are called. The caller is
* responsible for ensuring that the returned stream is closed.
* <li><b>Convenience methods:</b> These are implementations of common
* operations that are typically implemented by opening a stream using one of
* the methods in the first category, doing something and finally closing the
* stream or channel that was opened.
* </ul>
*
* @since 14.0
* @author Colin Decker
*/
public abstract class ByteSink implements OutputSupplier<OutputStream> {
/**
* Constructor for use by subclasses.
*/
protected ByteSink() {
}
/**
* Returns a {@link CharSink} view of this {@code ByteSink} that writes
* characters to this sink as bytes encoded with the given {@link Charset
* charset}.
*/
public CharSink asCharSink(Charset charset) {
return new AsCharSink(charset);
}
/**
* Opens a new {@link OutputStream} for writing to this sink. This method should
* return a new, independent stream each time it is called.
*
* <p>
* The caller is responsible for ensuring that the returned stream is closed.
*
* @throws IOException if an I/O error occurs in the process of opening the
* stream
*/
public abstract OutputStream 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 OutputSupplier} 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 OutputStream getOutput() throws IOException {
return openStream();
}
/**
* Opens a new buffered {@link OutputStream} for writing to this sink. The
* returned stream is not required to be a {@link BufferedOutputStream} in order
* to allow implementations to simply delegate to {@link #openStream()} when the
* stream returned by that method does not benefit from additional buffering
* (for example, a {@code ByteArrayOutputStream}). This method should return a
* new, independent stream each time it is called.
*
* <p>
* The caller is responsible for ensuring that the returned stream is closed.
*
* @throws IOException if an I/O error occurs in the process of opening the
* stream
* @since 15.0 (in 14.0 with return type {@link BufferedOutputStream})
*/
public OutputStream openBufferedStream() throws IOException {
OutputStream out = openStream();
return (out instanceof BufferedOutputStream) ? (BufferedOutputStream) out : new BufferedOutputStream(out);
}
/**
* Writes all the given bytes to this sink.
*
* @throws IOException if an I/O occurs in the process of writing to this sink
*/
public void write(byte[] bytes) throws IOException {
checkNotNull(bytes);
Closer closer = Closer.create();
try {
OutputStream out = closer.register(openStream());
out.write(bytes);
out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Writes all the bytes from the given {@code InputStream} to this sink. Does
* not close {@code input}.
*
* @throws IOException if an I/O occurs in the process of reading from
* {@code input} or writing to this sink
*/
public long writeFrom(InputStream input) throws IOException {
checkNotNull(input);
Closer closer = Closer.create();
try {
OutputStream out = closer.register(openStream());
long written = ByteStreams.copy(input, out);
out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330
return written;
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* A char sink that encodes written characters with a charset and writes
* resulting bytes to this byte sink.
*/
private final class AsCharSink extends CharSink {
private final Charset charset;
private AsCharSink(Charset charset) {
this.charset = checkNotNull(charset);
}
@Override
public Writer openStream() throws IOException {
return new OutputStreamWriter(ByteSink.this.openStream(), charset);
}
@Override
public String toString() {
return ByteSink.this.toString() + ".asCharSink(" + charset + ")";
}
}
}

View File

@ -0,0 +1,652 @@
/*
* 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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Iterator;
import com.google.common.annotations.Beta;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
import com.google.common.hash.Funnels;
import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hasher;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
/**
* A readable source of bytes, such as a file. Unlike an {@link InputStream}, a
* {@code ByteSource} is not an open, stateful stream for input that can be read
* and closed. Instead, it is an immutable <i>supplier</i> of
* {@code InputStream} instances.
*
* <p>
* {@code ByteSource} provides two kinds of methods:
* <ul>
* <li><b>Methods that return a stream:</b> These methods should return a
* <i>new</i>, independent instance each time they are called. The caller is
* responsible for ensuring that the returned stream is closed.
* <li><b>Convenience methods:</b> These are implementations of common
* operations that are typically implemented by opening a stream using one of
* the methods in the first category, doing something and finally closing the
* stream that was opened.
* </ul>
*
* @since 14.0
* @author Colin Decker
*/
public abstract class ByteSource implements InputSupplier<InputStream> {
private static final int BUF_SIZE = 0x1000; // 4K
/**
* Constructor for use by subclasses.
*/
protected ByteSource() {
}
/**
* Returns a {@link CharSource} view of this byte source that decodes bytes read
* from this source as characters using the given {@link Charset}.
*/
public CharSource asCharSource(Charset charset) {
return new AsCharSource(charset);
}
/**
* Opens a new {@link InputStream} for reading from this source. This method
* should return a new, independent stream each time it is called.
*
* <p>
* The caller is responsible for ensuring that the returned stream is closed.
*
* @throws IOException if an I/O error occurs in the process of opening the
* stream
*/
public abstract InputStream 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 InputStream getInput() throws IOException {
return openStream();
}
/**
* Opens a new buffered {@link InputStream} for reading from this source. The
* returned stream is not required to be a {@link BufferedInputStream} in order
* to allow implementations to simply delegate to {@link #openStream()} when the
* stream returned by that method does not benefit from additional buffering
* (for example, a {@code ByteArrayInputStream}). This method should return a
* new, independent stream each time it is called.
*
* <p>
* The caller is responsible for ensuring that the returned stream is closed.
*
* @throws IOException if an I/O error occurs in the process of opening the
* stream
* @since 15.0 (in 14.0 with return type {@link BufferedInputStream})
*/
public InputStream openBufferedStream() throws IOException {
InputStream in = openStream();
return (in instanceof BufferedInputStream) ? (BufferedInputStream) in : new BufferedInputStream(in);
}
/**
* Returns a view of a slice of this byte source that is at most {@code length}
* bytes long starting at the given {@code offset}.
*
* @throws IllegalArgumentException if {@code offset} or {@code length} is
* negative
*/
public ByteSource slice(long offset, long length) {
return new SlicedByteSource(offset, length);
}
/**
* Returns whether the source has zero bytes. The default implementation is to
* open a stream and check for EOF.
*
* @throws IOException if an I/O error occurs
* @since 15.0
*/
public boolean isEmpty() throws IOException {
Closer closer = Closer.create();
try {
InputStream in = closer.register(openStream());
return in.read() == -1;
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Returns the size of this source in bytes. For most implementations, this is a
* heavyweight operation that will open a stream, read (or
* {@link InputStream#skip(long) skip}, if possible) to the end of the stream
* and return the total number of bytes that were read.
*
* <p>
* For some sources, such as a file, this method may use a more efficient
* implementation. Note that in such cases, it is <i>possible</i> that this
* method will return a different number of bytes than would be returned by
* reading all of the bytes (for example, some special files may return a size
* of 0 despite actually having content when read).
*
* <p>
* In either case, if this is a mutable source such as a file, the size it
* returns may not be the same number of bytes a subsequent read would return.
*
* @throws IOException if an I/O error occurs in the process of reading the size
* of this source
*/
public long size() throws IOException {
Closer closer = Closer.create();
try {
InputStream in = closer.register(openStream());
return countBySkipping(in);
} catch (IOException e) {
// skip may not be supported... at any rate, try reading
} finally {
closer.close();
}
closer = Closer.create();
try {
InputStream in = closer.register(openStream());
return countByReading(in);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Counts the bytes in the given input stream using skip if possible. Returns
* SKIP_FAILED if the first call to skip threw, in which case skip may just not
* be supported.
*/
private long countBySkipping(InputStream in) throws IOException {
long count = 0;
while (true) {
// don't try to skip more than available()
// things may work really wrong with FileInputStream otherwise
long skipped = in.skip(Math.min(in.available(), Integer.MAX_VALUE));
if (skipped <= 0) {
if (in.read() == -1) {
return count;
} else if (count == 0 && in.available() == 0) {
// if available is still zero after reading a single byte, it
// will probably always be zero, so we should countByReading
throw new IOException();
}
count++;
} else {
count += skipped;
}
}
}
private static final byte[] countBuffer = new byte[BUF_SIZE];
private long countByReading(InputStream in) throws IOException {
long count = 0;
long read;
while ((read = in.read(countBuffer)) != -1) {
count += read;
}
return count;
}
/**
* Copies the contents of this byte source to the given {@code OutputStream}.
* Does not close {@code output}.
*
* @throws IOException if an I/O error occurs in the process of reading from
* this source or writing to {@code output}
*/
public long copyTo(OutputStream output) throws IOException {
checkNotNull(output);
Closer closer = Closer.create();
try {
InputStream in = closer.register(openStream());
return ByteStreams.copy(in, output);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Copies the contents of this byte source to the given {@code ByteSink}.
*
* @throws IOException if an I/O error occurs in the process of reading from
* this source or writing to {@code sink}
*/
public long copyTo(ByteSink sink) throws IOException {
checkNotNull(sink);
Closer closer = Closer.create();
try {
InputStream in = closer.register(openStream());
OutputStream out = closer.register(sink.openStream());
return ByteStreams.copy(in, out);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Reads the full contents of this byte source as a byte array.
*
* @throws IOException if an I/O error occurs in the process of reading from
* this source
*/
public byte[] read() throws IOException {
Closer closer = Closer.create();
try {
InputStream in = closer.register(openStream());
return ByteStreams.toByteArray(in);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Reads the contents of this byte source using the given {@code processor} to
* process bytes as they are read. Stops when all bytes have been read or the
* consumer returns {@code false}. Returns the result produced by the processor.
*
* @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 <T> T read(ByteProcessor<T> processor) throws IOException {
checkNotNull(processor);
Closer closer = Closer.create();
try {
InputStream in = closer.register(openStream());
return ByteStreams.readBytes(in, processor);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Hashes the contents of this byte source using the given hash function.
*
* @throws IOException if an I/O error occurs in the process of reading from
* this source
*/
public HashCode hash(HashFunction hashFunction) throws IOException {
Hasher hasher = hashFunction.newHasher();
copyTo(Funnels.asOutputStream(hasher));
return hasher.hash();
}
/**
* Checks that the contents of this byte source are equal to the contents of the
* given byte source.
*
* @throws IOException if an I/O error occurs in the process of reading from
* this source or {@code other}
*/
public boolean contentEquals(ByteSource other) throws IOException {
checkNotNull(other);
byte[] buf1 = new byte[BUF_SIZE];
byte[] buf2 = new byte[BUF_SIZE];
Closer closer = Closer.create();
try {
InputStream in1 = closer.register(openStream());
InputStream in2 = closer.register(other.openStream());
while (true) {
int read1 = ByteStreams.read(in1, buf1, 0, BUF_SIZE);
int read2 = ByteStreams.read(in2, buf2, 0, BUF_SIZE);
if (read1 != read2 || !Arrays.equals(buf1, buf2)) {
return false;
} else if (read1 != BUF_SIZE) {
return true;
}
}
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Concatenates multiple {@link ByteSource} instances into a single source.
* Streams returned from the source will contain the concatenated data from the
* streams of the underlying sources.
*
* <p>
* 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 ByteSource} containing the concatenated data
* @since 15.0
*/
public static ByteSource concat(Iterable<? extends ByteSource> sources) {
return new ConcatenatedByteSource(sources);
}
/**
* Concatenates multiple {@link ByteSource} instances into a single source.
* Streams returned from the source will contain the concatenated data from the
* streams of the underlying sources.
*
* <p>
* Only one underlying stream will be open at a time. Closing the concatenated
* stream will close the open underlying stream.
*
* <p>
* 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 ByteSource} containing the concatenated data
* @throws NullPointerException if any of {@code sources} is {@code null}
* @since 15.0
*/
public static ByteSource concat(Iterator<? extends ByteSource> sources) {
return concat(ImmutableList.copyOf(sources));
}
/**
* Concatenates multiple {@link ByteSource} instances into a single source.
* Streams returned from the source will contain the concatenated data from the
* streams of the underlying sources.
*
* <p>
* 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 ByteSource} containing the concatenated data
* @throws NullPointerException if any of {@code sources} is {@code null}
* @since 15.0
*/
public static ByteSource concat(ByteSource... sources) {
return concat(ImmutableList.copyOf(sources));
}
/**
* Returns a view of the given byte array as a {@link ByteSource}. To view only
* a specific range in the array, use
* {@code ByteSource.wrap(b).slice(offset, length)}.
*
* @since 15.0 (since 14.0 as {@code ByteStreams.asByteSource(byte[])}).
*/
public static ByteSource wrap(byte[] b) {
return new ByteArrayByteSource(b);
}
/**
* Returns an immutable {@link ByteSource} that contains no bytes.
*
* @since 15.0
*/
public static ByteSource empty() {
return EmptyByteSource.INSTANCE;
}
/**
* A char source that reads bytes from this source and decodes them as
* characters using a charset.
*/
private final class AsCharSource extends CharSource {
private final Charset charset;
private AsCharSource(Charset charset) {
this.charset = checkNotNull(charset);
}
@Override
public Reader openStream() throws IOException {
return new InputStreamReader(ByteSource.this.openStream(), charset);
}
@Override
public String toString() {
return ByteSource.this.toString() + ".asCharSource(" + charset + ")";
}
}
/**
* A view of a subsection of the containing byte source.
*/
private final class SlicedByteSource extends ByteSource {
private final long offset;
private final long length;
private SlicedByteSource(long offset, long length) {
checkArgument(offset >= 0, "offset (%s) may not be negative", offset);
checkArgument(length >= 0, "length (%s) may not be negative", length);
this.offset = offset;
this.length = length;
}
@Override
public InputStream openStream() throws IOException {
return sliceStream(ByteSource.this.openStream());
}
@Override
public InputStream openBufferedStream() throws IOException {
return sliceStream(ByteSource.this.openBufferedStream());
}
private InputStream sliceStream(InputStream in) throws IOException {
if (offset > 0) {
try {
ByteStreams.skipFully(in, offset);
} catch (Throwable e) {
Closer closer = Closer.create();
closer.register(in);
try {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
}
return ByteStreams.limit(in, length);
}
@Override
public ByteSource slice(long offset, long length) {
checkArgument(offset >= 0, "offset (%s) may not be negative", offset);
checkArgument(length >= 0, "length (%s) may not be negative", length);
long maxLength = this.length - offset;
return ByteSource.this.slice(this.offset + offset, Math.min(length, maxLength));
}
@Override
public boolean isEmpty() throws IOException {
return length == 0 || super.isEmpty();
}
@Override
public String toString() {
return ByteSource.this.toString() + ".slice(" + offset + ", " + length + ")";
}
}
private static class ByteArrayByteSource extends ByteSource {
protected final byte[] bytes;
protected ByteArrayByteSource(byte[] bytes) {
this.bytes = checkNotNull(bytes);
}
@Override
public InputStream openStream() {
return new EaglerInputStream(bytes);
}
@Override
public InputStream openBufferedStream() throws IOException {
return openStream();
}
@Override
public boolean isEmpty() {
return bytes.length == 0;
}
@Override
public long size() {
return bytes.length;
}
@Override
public byte[] read() {
return bytes.clone();
}
@Override
public long copyTo(OutputStream output) throws IOException {
output.write(bytes);
return bytes.length;
}
@Override
public <T> T read(ByteProcessor<T> processor) throws IOException {
processor.processBytes(bytes, 0, bytes.length);
return processor.getResult();
}
@Override
public HashCode hash(HashFunction hashFunction) throws IOException {
return hashFunction.hashBytes(bytes);
}
// TODO(user): Possibly override slice()
@Override
public String toString() {
return "ByteSource.wrap(" + Ascii.truncate(BaseEncoding.base16().encode(bytes), 30, "...") + ")";
}
}
private static final class EmptyByteSource extends ByteArrayByteSource {
private static final EmptyByteSource INSTANCE = new EmptyByteSource();
private EmptyByteSource() {
super(new byte[0]);
}
@Override
public CharSource asCharSource(Charset charset) {
checkNotNull(charset);
return CharSource.empty();
}
@Override
public byte[] read() {
return bytes; // length is 0, no need to clone
}
@Override
public String toString() {
return "ByteSource.empty()";
}
}
private static final class ConcatenatedByteSource extends ByteSource {
private final Iterable<? extends ByteSource> sources;
ConcatenatedByteSource(Iterable<? extends ByteSource> sources) {
this.sources = checkNotNull(sources);
}
@Override
public InputStream openStream() throws IOException {
return new MultiInputStream(sources.iterator());
}
@Override
public boolean isEmpty() throws IOException {
for (ByteSource source : sources) {
if (!source.isEmpty()) {
return false;
}
}
return true;
}
@Override
public long size() throws IOException {
long result = 0L;
for (ByteSource source : sources) {
result += source.size();
}
return result;
}
@Override
public String toString() {
return "ByteSource.concat(" + sources + ")";
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2013 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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkPositionIndexes;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
/**
* A {@link Reader} that reads the characters in a {@link CharSequence}. Like
* {@code StringReader}, but works with any {@link CharSequence}.
*
* @author Colin Decker
*/
// TODO(user): make this public? as a type, or a method in CharStreams?
final class CharSequenceReader extends Reader {
private CharSequence seq;
private int pos;
private int mark;
/**
* Creates a new reader wrapping the given character sequence.
*/
public CharSequenceReader(CharSequence seq) {
this.seq = checkNotNull(seq);
}
private void checkOpen() throws IOException {
if (seq == null) {
throw new IOException("reader closed");
}
}
private boolean hasRemaining() {
return remaining() > 0;
}
private int remaining() {
return seq.length() - pos;
}
@Override
public synchronized int read(CharBuffer target) throws IOException {
checkNotNull(target);
checkOpen();
if (!hasRemaining()) {
return -1;
}
int charsToRead = Math.min(target.remaining(), remaining());
for (int i = 0; i < charsToRead; i++) {
target.put(seq.charAt(pos++));
}
return charsToRead;
}
@Override
public synchronized int read() throws IOException {
checkOpen();
return hasRemaining() ? seq.charAt(pos++) : -1;
}
@Override
public synchronized int read(char[] cbuf, int off, int len) throws IOException {
checkPositionIndexes(off, off + len, cbuf.length);
checkOpen();
if (!hasRemaining()) {
return -1;
}
int charsToRead = Math.min(len, remaining());
for (int i = 0; i < charsToRead; i++) {
cbuf[off + i] = seq.charAt(pos++);
}
return charsToRead;
}
@Override
public synchronized long skip(long n) throws IOException {
checkArgument(n >= 0, "n (%s) may not be negative", n);
checkOpen();
int charsToSkip = (int) Math.min(remaining(), n); // safe because remaining is an int
pos += charsToSkip;
return charsToSkip;
}
@Override
public synchronized boolean ready() throws IOException {
checkOpen();
return true;
}
@Override
public boolean markSupported() {
return true;
}
@Override
public synchronized void mark(int readAheadLimit) throws IOException {
checkArgument(readAheadLimit >= 0, "readAheadLimit (%s) may not be negative", readAheadLimit);
checkOpen();
mark = pos;
}
@Override
public synchronized void reset() throws IOException {
checkOpen();
pos = mark;
}
@Override
public synchronized void close() throws IOException {
seq = null;
}
}

View File

@ -0,0 +1,190 @@
/*
* 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.BufferedWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.Charset;
/**
* A destination to which characters can be written, such as a text file. Unlike
* a {@link Writer}, a {@code CharSink} is not an open, stateful stream that can
* be written to and closed. Instead, it is an immutable <i>supplier</i> of
* {@code Writer} instances.
*
* <p>
* {@code CharSink} provides two kinds of methods:
* <ul>
* <li><b>Methods that return a writer:</b> These methods should return a
* <i>new</i>, independent instance each time they are called. The caller is
* responsible for ensuring that the returned writer is closed.
* <li><b>Convenience methods:</b> These are implementations of common
* operations that are typically implemented by opening a writer using one of
* the methods in the first category, doing something and finally closing the
* writer that was opened.
* </ul>
*
* <p>
* Any {@link ByteSink} may be viewed as a {@code CharSink} with a specific
* {@linkplain Charset character encoding} using
* {@link ByteSink#asCharSink(Charset)}. Characters written to the resulting
* {@code CharSink} will written to the {@code ByteSink} as encoded bytes.
*
* @since 14.0
* @author Colin Decker
*/
public abstract class CharSink implements OutputSupplier<Writer> {
/**
* Constructor for use by subclasses.
*/
protected CharSink() {
}
/**
* Opens a new {@link Writer} for writing to this sink. This method should
* return a new, independent writer each time it is called.
*
* <p>
* The caller is responsible for ensuring that the returned writer is closed.
*
* @throws IOException if an I/O error occurs in the process of opening the
* writer
*/
public abstract Writer 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 OutputSupplier} 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 Writer getOutput() throws IOException {
return openStream();
}
/**
* Opens a new buffered {@link Writer} for writing to this sink. The returned
* stream is not required to be a {@link BufferedWriter} in order to allow
* implementations to simply delegate to {@link #openStream()} when the stream
* returned by that method does not benefit from additional buffering. This
* method should return a new, independent writer each time it is called.
*
* <p>
* The caller is responsible for ensuring that the returned writer is closed.
*
* @throws IOException if an I/O error occurs in the process of opening the
* writer
* @since 15.0 (in 14.0 with return type {@link BufferedWriter})
*/
public Writer openBufferedStream() throws IOException {
Writer writer = openStream();
return (writer instanceof BufferedWriter) ? (BufferedWriter) writer : new BufferedWriter(writer);
}
/**
* Writes the given character sequence to this sink.
*
* @throws IOException if an I/O error in the process of writing to this sink
*/
public void write(CharSequence charSequence) throws IOException {
checkNotNull(charSequence);
Closer closer = Closer.create();
try {
Writer out = closer.register(openStream());
out.append(charSequence);
out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Writes the given lines of text to this sink with each line (including the
* last) terminated with the operating system's default line separator. This
* method is equivalent to
* {@code writeLines(lines, System.getProperty("line.separator"))}.
*
* @throws IOException if an I/O error occurs in the process of writing to this
* sink
*/
public void writeLines(Iterable<? extends CharSequence> lines) throws IOException {
writeLines(lines, System.getProperty("line.separator"));
}
/**
* Writes the given lines of text to this sink with each line (including the
* last) terminated with the given line separator.
*
* @throws IOException if an I/O error occurs in the process of writing to this
* sink
*/
public void writeLines(Iterable<? extends CharSequence> lines, String lineSeparator) throws IOException {
checkNotNull(lines);
checkNotNull(lineSeparator);
Closer closer = Closer.create();
try {
Writer out = closer.register(openBufferedStream());
for (CharSequence line : lines) {
out.append(line).append(lineSeparator);
}
out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Writes all the text from the given {@link Readable} (such as a
* {@link Reader}) to this sink. Does not close {@code readable} if it is
* {@code Closeable}.
*
* @throws IOException if an I/O error occurs in the process of reading from
* {@code readable} or writing to this sink
*/
public long writeFrom(Readable readable) throws IOException {
checkNotNull(readable);
Closer closer = Closer.create();
try {
Writer out = closer.register(openStream());
long written = CharStreams.copy(readable, out);
out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330
return written;
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
}

View File

@ -0,0 +1,493 @@
/*
* 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
* <i>supplier</i> of {@code Reader} instances.
*
* <p>
* {@code CharSource} provides two kinds of methods:
* <ul>
* <li><b>Methods that return a reader:</b> These methods should return a
* <i>new</i>, independent instance each time they are called. The caller is
* responsible for ensuring that the returned reader is closed.
* <li><b>Convenience methods:</b> These are implementations of common
* operations that are typically implemented by opening a reader using one of
* the methods in the first category, doing something and finally closing the
* reader that was opened.
* </ul>
*
* <p>
* 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.
*
* <p>
* 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<Reader> {
/**
* Constructor for use by subclasses.
*/
protected CharSource() {
}
/**
* Opens a new {@link Reader} for reading from this source. This method should
* return a new, independent reader each time it is called.
*
* <p>
* 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.
*
* <p>
* 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.
*
* <p>
* 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.
*
* <p>
* 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<String> readLines() throws IOException {
Closer closer = Closer.create();
try {
BufferedReader reader = closer.register(openBufferedStream());
List<String> result = Lists.newArrayList();
String line;
while ((line = reader.readLine()) != null) {
result.add(line);
}
return ImmutableList.copyOf(result);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Reads lines of text from this source, processing each line as it is read
* using the given {@link LineProcessor processor}. Stops when all lines have
* been processed or the processor returns {@code false} and returns the result
* produced by the processor.
*
* <p>
* 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 <T> T readLines(LineProcessor<T> processor) throws IOException {
checkNotNull(processor);
Closer closer = Closer.create();
try {
Reader reader = closer.register(openStream());
return CharStreams.readLines(reader, processor);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Returns whether the source has zero chars. The default implementation is to
* open a stream and check for EOF.
*
* @throws IOException if an I/O error occurs
* @since 15.0
*/
public boolean isEmpty() throws IOException {
Closer closer = Closer.create();
try {
Reader reader = closer.register(openStream());
return reader.read() == -1;
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* 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.
*
* <p>
* 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.
*
* <p>
* Only one underlying stream will be open at a time. Closing the concatenated
* stream will close the open underlying stream.
*
* <p>
* 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.
*
* <p>
* 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<String> lines() {
return new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return new AbstractIterator<String>() {
Iterator<String> lines = LINE_SPLITTER.split(seq).iterator();
@Override
protected String computeNext() {
if (lines.hasNext()) {
String next = lines.next();
// skip last line if it's empty
if (lines.hasNext() || !next.isEmpty()) {
return next;
}
}
return endOfData();
}
};
}
};
}
@Override
public String readFirstLine() {
Iterator<String> lines = lines().iterator();
return lines.hasNext() ? lines.next() : null;
}
@Override
public ImmutableList<String> readLines() {
return ImmutableList.copyOf(lines());
}
@Override
public <T> T readLines(LineProcessor<T> processor) throws IOException {
for (String line : lines()) {
if (!processor.processLine(line)) {
break;
}
}
return processor.getResult();
}
@Override
public String toString() {
return "CharSource.wrap(" + Ascii.truncate(seq, 30, "...") + ")";
}
}
private static final class EmptyCharSource extends CharSequenceCharSource {
private static final EmptyCharSource INSTANCE = new EmptyCharSource();
private EmptyCharSource() {
super("");
}
@Override
public String toString() {
return "CharSource.empty()";
}
}
private static final class ConcatenatedCharSource extends CharSource {
private final Iterable<? extends CharSource> sources;
ConcatenatedCharSource(Iterable<? extends CharSource> sources) {
this.sources = checkNotNull(sources);
}
@Override
public Reader openStream() throws IOException {
return new MultiReader(sources.iterator());
}
@Override
public boolean isEmpty() throws IOException {
for (CharSource source : sources) {
if (!source.isEmpty()) {
return false;
}
}
return true;
}
@Override
public String toString() {
return "CharSource.concat(" + sources + ")";
}
}
}

View File

@ -0,0 +1,597 @@
/*
* Copyright (C) 2007 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 static com.google.common.base.Preconditions.checkPositionIndexes;
import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.google.common.annotations.Beta;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
/**
* Provides utility methods for working with character streams.
*
* <p>
* All method parameters must be non-null unless documented otherwise.
*
* <p>
* Some of the methods in this class take arguments with a generic type of
* {@code Readable & Closeable}. A {@link java.io.Reader} implements both of
* those interfaces. Similarly for {@code Appendable & Closeable} and
* {@link java.io.Writer}.
*
* @author Chris Nokleberg
* @author Bin Zhu
* @author Colin Decker
* @since 1.0
*/
@Beta
public final class CharStreams {
private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes)
private CharStreams() {
}
/**
* Returns a factory that will supply instances of {@link StringReader} that
* read a string value.
*
* @param value the string to read
* @return the factory
* @deprecated Use {@link CharSource#wrap(CharSequence)} instead. This method is
* scheduled for removal in Guava 18.0.
*/
@Deprecated
public static InputSupplier<StringReader> newReaderSupplier(final String value) {
return asInputSupplier(CharSource.wrap(value));
}
/**
* Returns a factory that will supply instances of {@link InputStreamReader},
* using the given {@link InputStream} factory and character set.
*
* @param in the factory that will be used to open input streams
* @param charset the charset used to decode the input stream; see
* {@link Charsets} for helpful predefined constants
* @return the factory
* @deprecated Use {@link ByteSource#asCharSource(Charset)} instead. This method
* is scheduled for removal in Guava 18.0.
*/
@Deprecated
public static InputSupplier<InputStreamReader> newReaderSupplier(final InputSupplier<? extends InputStream> in,
final Charset charset) {
return asInputSupplier(ByteStreams.asByteSource(in).asCharSource(charset));
}
/**
* Returns a factory that will supply instances of {@link OutputStreamWriter},
* using the given {@link OutputStream} factory and character set.
*
* @param out the factory that will be used to open output streams
* @param charset the charset used to encode the output stream; see
* {@link Charsets} for helpful predefined constants
* @return the factory
* @deprecated Use {@link ByteSink#asCharSink(Charset)} instead. This method is
* scheduled for removal in Guava 18.0.
*/
@Deprecated
public static OutputSupplier<OutputStreamWriter> newWriterSupplier(final OutputSupplier<? extends OutputStream> out,
final Charset charset) {
return asOutputSupplier(ByteStreams.asByteSink(out).asCharSink(charset));
}
/**
* Writes a character sequence (such as a string) to an appendable object from
* the given supplier.
*
* @param from the character sequence to write
* @param to the output supplier
* @throws IOException if an I/O error occurs
* @deprecated Use {@link CharSink#write(CharSequence)} instead. This method is
* scheduled for removal in Guava 18.0.
*/
@Deprecated
public static <W extends Appendable & Closeable> void write(CharSequence from, OutputSupplier<W> to)
throws IOException {
asCharSink(to).write(from);
}
/**
* Opens {@link Readable} and {@link Appendable} objects from the given
* factories, copies all characters between the two, and closes them.
*
* @param from the input factory
* @param to the output factory
* @return the number of characters copied
* @throws IOException if an I/O error occurs
* @deprecated Use {@link CharSource#copyTo(CharSink)} instead. This method is
* scheduled for removal in Guava 18.0.
*/
@Deprecated
public static <R extends Readable & Closeable, W extends Appendable & Closeable> long copy(InputSupplier<R> from,
OutputSupplier<W> to) throws IOException {
return asCharSource(from).copyTo(asCharSink(to));
}
/**
* Opens a {@link Readable} object from the supplier, copies all characters to
* the {@link Appendable} object, and closes the input. Does not close or flush
* the output.
*
* @param from the input factory
* @param to the object to write to
* @return the number of characters copied
* @throws IOException if an I/O error occurs
* @deprecated Use {@link CharSource#copyTo(Appendable)} instead. This method is
* scheduled for removal in Guava 18.0.
*/
@Deprecated
public static <R extends Readable & Closeable> long copy(InputSupplier<R> from, Appendable to) throws IOException {
return asCharSource(from).copyTo(to);
}
/**
* Copies all characters between the {@link Readable} and {@link Appendable}
* objects. Does not close or flush either object.
*
* @param from the object to read from
* @param to the object to write to
* @return the number of characters copied
* @throws IOException if an I/O error occurs
*/
public static long copy(Readable from, Appendable to) throws IOException {
checkNotNull(from);
checkNotNull(to);
CharBuffer buf = CharBuffer.allocate(BUF_SIZE);
long total = 0;
while (from.read(buf) != -1) {
buf.flip();
to.append(buf);
total += buf.remaining();
buf.clear();
}
return total;
}
/**
* Reads all characters from a {@link Readable} object into a {@link String}.
* Does not close the {@code Readable}.
*
* @param r the object to read from
* @return a string containing all the characters
* @throws IOException if an I/O error occurs
*/
public static String toString(Readable r) throws IOException {
return toStringBuilder(r).toString();
}
/**
* Returns the characters from a {@link Readable} & {@link Closeable} object
* supplied by a factory as a {@link String}.
*
* @param supplier the factory to read from
* @return a string containing all the characters
* @throws IOException if an I/O error occurs
* @deprecated Use {@link CharSource#read()} instead. This method is scheduled
* for removal in Guava 18.0.
*/
@Deprecated
public static <R extends Readable & Closeable> String toString(InputSupplier<R> supplier) throws IOException {
return asCharSource(supplier).read();
}
/**
* Reads all characters from a {@link Readable} object into a new
* {@link StringBuilder} instance. Does not close the {@code Readable}.
*
* @param r the object to read from
* @return a {@link StringBuilder} containing all the characters
* @throws IOException if an I/O error occurs
*/
private static StringBuilder toStringBuilder(Readable r) throws IOException {
StringBuilder sb = new StringBuilder();
copy(r, sb);
return sb;
}
/**
* Reads the first line from a {@link Readable} & {@link Closeable} object
* supplied by a factory. The line does not include line-termination characters,
* but does include other leading and trailing whitespace.
*
* @param supplier the factory to read from
* @return the first line, or null if the reader is empty
* @throws IOException if an I/O error occurs
* @deprecated Use {@link CharSource#readFirstLine()} instead. This method is
* scheduled for removal in Guava 18.0.
*/
@Deprecated
public static <R extends Readable & Closeable> String readFirstLine(InputSupplier<R> supplier) throws IOException {
return asCharSource(supplier).readFirstLine();
}
/**
* Reads all of the lines from a {@link Readable} & {@link Closeable} object
* supplied by a factory. The lines do not include line-termination characters,
* but do include other leading and trailing whitespace.
*
* @param supplier the factory to read from
* @return a mutable {@link List} containing all the lines
* @throws IOException if an I/O error occurs
* @deprecated Use {@link CharSource#readLines()} instead, but note that it
* returns an {@code ImmutableList}. This method is scheduled for
* removal in Guava 18.0.
*/
@Deprecated
public static <R extends Readable & Closeable> List<String> readLines(InputSupplier<R> supplier)
throws IOException {
Closer closer = Closer.create();
try {
R r = closer.register(supplier.getInput());
return readLines(r);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Reads all of the lines from a {@link Readable} object. The lines do not
* include line-termination characters, but do include other leading and
* trailing whitespace.
*
* <p>
* Does not close the {@code Readable}. If reading files or resources you should
* use the {@link Files#readLines} and {@link Resources#readLines} methods.
*
* @param r the object to read from
* @return a mutable {@link List} containing all the lines
* @throws IOException if an I/O error occurs
*/
public static List<String> readLines(Readable r) throws IOException {
List<String> result = new ArrayList<String>();
LineReader lineReader = new LineReader(r);
String line;
while ((line = lineReader.readLine()) != null) {
result.add(line);
}
return result;
}
/**
* Streams lines from a {@link Readable} object, stopping when the processor
* returns {@code false} or all lines have been read and returning the result
* produced by the processor. Does not close {@code readable}. Note that this
* method may not fully consume the contents of {@code readable} if the
* processor stops processing early.
*
* @throws IOException if an I/O error occurs
* @since 14.0
*/
public static <T> T readLines(Readable readable, LineProcessor<T> processor) throws IOException {
checkNotNull(readable);
checkNotNull(processor);
LineReader lineReader = new LineReader(readable);
String line;
while ((line = lineReader.readLine()) != null) {
if (!processor.processLine(line)) {
break;
}
}
return processor.getResult();
}
/**
* Streams lines from a {@link Readable} and {@link Closeable} object supplied
* by a factory, stopping when our callback returns false, or we have read all
* of the lines.
*
* @param supplier the factory to read from
* @param callback the LineProcessor to use to handle the lines
* @return the output of processing the lines
* @throws IOException if an I/O error occurs
* @deprecated Use {@link CharSource#readLines(LineProcessor)} instead. This
* method is scheduled for removal in Guava 18.0.
*/
@Deprecated
public static <R extends Readable & Closeable, T> T readLines(InputSupplier<R> supplier, LineProcessor<T> callback)
throws IOException {
checkNotNull(supplier);
checkNotNull(callback);
Closer closer = Closer.create();
try {
R r = closer.register(supplier.getInput());
return readLines(r, callback);
} catch (Throwable e) {
throw closer.rethrow(e);
} finally {
closer.close();
}
}
/**
* Joins multiple {@link Reader} suppliers into a single supplier. Reader
* returned from the supplier will contain the concatenated data from the
* readers of the underlying suppliers.
*
* <p>
* Reading from the joined reader will throw a {@link NullPointerException} if
* any of the suppliers are null or return null.
*
* <p>
* Only one underlying reader will be open at a time. Closing the joined reader
* will close the open underlying reader.
*
* @param suppliers the suppliers to concatenate
* @return a supplier that will return a reader containing the concatenated data
* @deprecated Use {@link CharSource#concat(Iterable)} instead. This method is
* scheduled for removal in Guava 18.0.
*/
@Deprecated
public static InputSupplier<Reader> join(final Iterable<? extends InputSupplier<? extends Reader>> suppliers) {
checkNotNull(suppliers);
Iterable<CharSource> sources = Iterables.transform(suppliers,
new Function<InputSupplier<? extends Reader>, CharSource>() {
@Override
public CharSource apply(InputSupplier<? extends Reader> input) {
return asCharSource(input);
}
});
return asInputSupplier(CharSource.concat(sources));
}
/**
* Varargs form of {@link #join(Iterable)}.
*
* @deprecated Use {@link CharSource#concat(CharSource[])} instead. This method
* is scheduled for removal in Guava 18.0.
*/
@Deprecated
@SuppressWarnings("unchecked") // suppress "possible heap pollution" warning in JDK7
public static InputSupplier<Reader> join(InputSupplier<? extends Reader>... suppliers) {
return join(Arrays.asList(suppliers));
}
/**
* Discards {@code n} characters of data from the reader. This method will block
* until the full amount has been skipped. Does not close the reader.
*
* @param reader the reader to read from
* @param n the number of characters to skip
* @throws EOFException if this stream reaches the end before skipping all the
* characters
* @throws IOException if an I/O error occurs
*/
public static void skipFully(Reader reader, long n) throws IOException {
checkNotNull(reader);
while (n > 0) {
long amt = reader.skip(n);
if (amt == 0) {
// force a blocking read
if (reader.read() == -1) {
throw new EOFException();
}
n--;
} else {
n -= amt;
}
}
}
/**
* Returns a {@link Writer} that simply discards written chars.
*
* @since 15.0
*/
public static Writer nullWriter() {
return NullWriter.INSTANCE;
}
private static final class NullWriter extends Writer {
private static final NullWriter INSTANCE = new NullWriter();
@Override
public void write(int c) {
}
@Override
public void write(char[] cbuf) {
checkNotNull(cbuf);
}
@Override
public void write(char[] cbuf, int off, int len) {
checkPositionIndexes(off, off + len, cbuf.length);
}
@Override
public void write(String str) {
checkNotNull(str);
}
@Override
public void write(String str, int off, int len) {
checkPositionIndexes(off, off + len, str.length());
}
@Override
public Writer append(CharSequence csq) {
checkNotNull(csq);
return this;
}
@Override
public Writer append(CharSequence csq, int start, int end) {
checkPositionIndexes(start, end, csq.length());
return this;
}
@Override
public Writer append(char c) {
return this;
}
@Override
public void flush() {
}
@Override
public void close() {
}
@Override
public String toString() {
return "CharStreams.nullWriter()";
}
}
/**
* Returns a Writer that sends all output to the given {@link Appendable}
* target. Closing the writer will close the target if it is {@link Closeable},
* and flushing the writer will flush the target if it is
* {@link java.io.Flushable}.
*
* @param target the object to which output will be sent
* @return a new Writer object, unless target is a Writer, in which case the
* target is returned
*/
public static Writer asWriter(Appendable target) {
if (target instanceof Writer) {
return (Writer) target;
}
return new AppendableWriter(target);
}
// TODO(user): Remove these once Input/OutputSupplier methods are removed
static Reader asReader(final Readable readable) {
checkNotNull(readable);
if (readable instanceof Reader) {
return (Reader) readable;
}
return new Reader() {
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return read(CharBuffer.wrap(cbuf, off, len));
}
@Override
public int read(CharBuffer target) throws IOException {
return readable.read(target);
}
@Override
public void close() throws IOException {
if (readable instanceof Closeable) {
((Closeable) readable).close();
}
}
};
}
/**
* Returns a view of the given {@code Readable} supplier as a
* {@code CharSource}.
*
* <p>
* This method is a temporary method provided for easing migration from
* suppliers to sources and sinks.
*
* @since 15.0
* @deprecated Convert all {@code InputSupplier<? extends Readable>}
* implementations to extend {@link CharSource} or provide a method
* for viewing the object as a {@code CharSource}. This method is
* scheduled for removal in Guava 18.0.
*/
@Deprecated
public static CharSource asCharSource(final InputSupplier<? extends Readable> supplier) {
checkNotNull(supplier);
return new CharSource() {
@Override
public Reader openStream() throws IOException {
return asReader(supplier.getInput());
}
@Override
public String toString() {
return "CharStreams.asCharSource(" + supplier + ")";
}
};
}
/**
* Returns a view of the given {@code Appendable} supplier as a
* {@code CharSink}.
*
* <p>
* This method is a temporary method provided for easing migration from
* suppliers to sources and sinks.
*
* @since 15.0
* @deprecated Convert all {@code OutputSupplier<? extends Appendable>}
* implementations to extend {@link CharSink} or provide a method
* for viewing the object as a {@code CharSink}. This method is
* scheduled for removal in Guava 18.0.
*/
@Deprecated
public static CharSink asCharSink(final OutputSupplier<? extends Appendable> supplier) {
checkNotNull(supplier);
return new CharSink() {
@Override
public Writer openStream() throws IOException {
return asWriter(supplier.getOutput());
}
@Override
public String toString() {
return "CharStreams.asCharSink(" + supplier + ")";
}
};
}
@SuppressWarnings("unchecked") // used internally where known to be safe
static <R extends Reader> InputSupplier<R> asInputSupplier(CharSource source) {
return (InputSupplier) checkNotNull(source);
}
@SuppressWarnings("unchecked") // used internally where known to be safe
static <W extends Writer> OutputSupplier<W> asOutputSupplier(CharSink sink) {
return (OutputSupplier) checkNotNull(sink);
}
}

View File

@ -0,0 +1,143 @@
/*
* Copyright (C) 2007 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 java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import com.google.common.annotations.Beta;
import com.google.common.annotations.VisibleForTesting;
/**
* Utility methods for working with {@link Closeable} objects.
*
* @author Michael Lancaster
* @since 1.0
*/
@Beta
public final class Closeables {
@VisibleForTesting
static final Logger logger = Logger.getLogger(Closeables.class.getName());
private Closeables() {
}
/**
* Closes a {@link Closeable}, with control over whether an {@code IOException}
* may be thrown. This is primarily useful in a finally block, where a thrown
* exception needs to be logged but not propagated (otherwise the original
* exception will be lost).
*
* <p>
* If {@code swallowIOException} is true then we never throw {@code IOException}
* but merely log it.
*
* <p>
* Example:
*
* <pre>
* {@code
*
* public void useStreamNicely() throws IOException {
* SomeStream stream = new SomeStream("foo");
* boolean threw = true;
* try {
* // ... code which does something with the stream ...
* threw = false;
* } finally {
* // If an exception occurs, rethrow it only if threw==false:
* Closeables.close(stream, threw);
* }
* }}
* </pre>
*
* @param closeable the {@code Closeable} object to be closed, or null,
* in which case this method does nothing
* @param swallowIOException if true, don't propagate IO exceptions thrown by
* the {@code close} methods
* @throws IOException if {@code swallowIOException} is false and {@code close}
* throws an {@code IOException}.
*/
public static void close(@Nullable Closeable closeable, boolean swallowIOException) throws IOException {
if (closeable == null) {
return;
}
try {
closeable.close();
} catch (IOException e) {
if (swallowIOException) {
logger.log(Level.WARNING, "IOException thrown while closing Closeable.", e);
} else {
throw e;
}
}
}
/**
* Closes the given {@link InputStream}, logging any {@code IOException} that's
* thrown rather than propagating it.
*
* <p>
* While it's not safe in the general case to ignore exceptions that are thrown
* when closing an I/O resource, it should generally be safe in the case of a
* resource that's being used only for reading, such as an {@code InputStream}.
* Unlike with writable resources, there's no chance that a failure that occurs
* when closing the stream indicates a meaningful problem such as a failure to
* flush all bytes to the underlying resource.
*
* @param inputStream the input stream to be closed, or {@code null} in which
* case this method does nothing
* @since 17.0
*/
public static void closeQuietly(@Nullable InputStream inputStream) {
try {
close(inputStream, true);
} catch (IOException impossible) {
throw new AssertionError(impossible);
}
}
/**
* Closes the given {@link Reader}, logging any {@code IOException} that's
* thrown rather than propagating it.
*
* <p>
* While it's not safe in the general case to ignore exceptions that are thrown
* when closing an I/O resource, it should generally be safe in the case of a
* resource that's being used only for reading, such as a {@code Reader}. Unlike
* with writable resources, there's no chance that a failure that occurs when
* closing the reader indicates a meaningful problem such as a failure to flush
* all bytes to the underlying resource.
*
* @param reader the reader to be closed, or {@code null} in which case this
* method does nothing
* @since 17.0
*/
public static void closeQuietly(@Nullable Reader reader) {
try {
close(reader, true);
} catch (IOException impossible) {
throw new AssertionError(impossible);
}
}
}

View File

@ -0,0 +1,324 @@
/*
* 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.Closeable;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.logging.Level;
import javax.annotation.Nullable;
import com.google.common.annotations.Beta;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
/**
* A {@link Closeable} that collects {@code Closeable} resources and closes them
* all when it is {@linkplain #close closed}. This is intended to approximately
* emulate the behavior of Java 7's <a href=
* "http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html">
* try-with-resources</a> statement in JDK6-compatible code. Running on Java 7,
* code using this should be approximately equivalent in behavior to the same
* code written with try-with-resources. Running on Java 6, exceptions that
* cannot be thrown must be logged rather than being added to the thrown
* exception as a suppressed exception.
*
* <p>
* This class is intended to be used in the following pattern:
*
* <pre>
* {
* &#64;code
* Closer closer = Closer.create();
* try {
* InputStream in = closer.register(openInputStream());
* OutputStream out = closer.register(openOutputStream());
* // do stuff
* } catch (Throwable e) {
* // ensure that any checked exception types other than IOException that could
* // be thrown are
* // provided here, e.g. throw closer.rethrow(e, CheckedException.class);
* throw closer.rethrow(e);
* } finally {
* closer.close();
* }
* }
* </pre>
*
* <p>
* Note that this try-catch-finally block is not equivalent to a
* try-catch-finally block using try-with-resources. To get the equivalent of
* that, you must wrap the above code in <i>another</i> try block in order to
* catch any exception that may be thrown (including from the call to
* {@code close()}).
*
* <p>
* This pattern ensures the following:
*
* <ul>
* <li>Each {@code Closeable} resource that is successfully registered will be
* closed later.</li>
* <li>If a {@code Throwable} is thrown in the try block, no exceptions that
* occur when attempting to close resources will be thrown from the finally
* block. The throwable from the try block will be thrown.</li>
* <li>If no exceptions or errors were thrown in the try block, the <i>first</i>
* exception thrown by an attempt to close a resource will be thrown.</li>
* <li>Any exception caught when attempting to close a resource that is
* <i>not</i> thrown (because another exception is already being thrown) is
* <i>suppressed</i>.</li>
* </ul>
*
* <p>
* An exception that is suppressed is not thrown. The method of suppression used
* depends on the version of Java the code is running on:
*
* <ul>
* <li><b>Java 7+:</b> Exceptions are suppressed by adding them to the exception
* that <i>will</i> be thrown using
* {@code Throwable.addSuppressed(Throwable)}.</li>
* <li><b>Java 6:</b> Exceptions are suppressed by logging them instead.</li>
* </ul>
*
* @author Colin Decker
* @since 14.0
*/
// Coffee's for {@link Closer closers} only.
@Beta
public final class Closer implements Closeable {
/**
* The suppressor implementation to use for the current Java version.
*/
private static final Suppressor SUPPRESSOR = SuppressingSuppressor.isAvailable() ? SuppressingSuppressor.INSTANCE
: LoggingSuppressor.INSTANCE;
/**
* Creates a new {@link Closer}.
*/
public static Closer create() {
return new Closer(SUPPRESSOR);
}
@VisibleForTesting
final Suppressor suppressor;
// only need space for 2 elements in most cases, so try to use the smallest
// array possible
private final Deque<Closeable> stack = new ArrayDeque<Closeable>(4);
private Throwable thrown;
@VisibleForTesting
Closer(Suppressor suppressor) {
this.suppressor = checkNotNull(suppressor); // checkNotNull to satisfy null tests
}
/**
* Registers the given {@code closeable} to be closed when this {@code Closer}
* is {@linkplain #close closed}.
*
* @return the given {@code closeable}
*/
// close. this word no longer has any meaning to me.
public <C extends Closeable> C register(@Nullable C closeable) {
if (closeable != null) {
stack.addFirst(closeable);
}
return closeable;
}
/**
* Stores the given throwable and rethrows it. It will be rethrown as is if it
* is an {@code IOException}, {@code RuntimeException} or {@code Error}.
* Otherwise, it will be rethrown wrapped in a {@code RuntimeException}.
* <b>Note:</b> Be sure to declare all of the checked exception types your try
* block can throw when calling an overload of this method so as to avoid losing
* the original exception type.
*
* <p>
* This method always throws, and as such should be called as
* {@code throw closer.rethrow(e);} to ensure the compiler knows that it will
* throw.
*
* @return this method does not return; it always throws
* @throws IOException when the given throwable is an IOException
*/
public RuntimeException rethrow(Throwable e) throws IOException {
checkNotNull(e);
thrown = e;
Throwables.propagateIfPossible(e, IOException.class);
throw new RuntimeException(e);
}
/**
* Stores the given throwable and rethrows it. It will be rethrown as is if it
* is an {@code IOException}, {@code RuntimeException}, {@code Error} or a
* checked exception of the given type. Otherwise, it will be rethrown wrapped
* in a {@code RuntimeException}. <b>Note:</b> Be sure to declare all of the
* checked exception types your try block can throw when calling an overload of
* this method so as to avoid losing the original exception type.
*
* <p>
* This method always throws, and as such should be called as
* {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it
* will throw.
*
* @return this method does not return; it always throws
* @throws IOException when the given throwable is an IOException
* @throws X when the given throwable is of the declared type X
*/
public <X extends Exception> RuntimeException rethrow(Throwable e, Class<X> declaredType) throws IOException, X {
checkNotNull(e);
thrown = e;
Throwables.propagateIfPossible(e, IOException.class);
Throwables.propagateIfPossible(e, declaredType);
throw new RuntimeException(e);
}
/**
* Stores the given throwable and rethrows it. It will be rethrown as is if it
* is an {@code IOException}, {@code RuntimeException}, {@code Error} or a
* checked exception of either of the given types. Otherwise, it will be
* rethrown wrapped in a {@code RuntimeException}. <b>Note:</b> Be sure to
* declare all of the checked exception types your try block can throw when
* calling an overload of this method so as to avoid losing the original
* exception type.
*
* <p>
* This method always throws, and as such should be called as
* {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it
* will throw.
*
* @return this method does not return; it always throws
* @throws IOException when the given throwable is an IOException
* @throws X1 when the given throwable is of the declared type X1
* @throws X2 when the given throwable is of the declared type X2
*/
public <X1 extends Exception, X2 extends Exception> RuntimeException rethrow(Throwable e, Class<X1> declaredType1,
Class<X2> declaredType2) throws IOException, X1, X2 {
checkNotNull(e);
thrown = e;
Throwables.propagateIfPossible(e, IOException.class);
Throwables.propagateIfPossible(e, declaredType1, declaredType2);
throw new RuntimeException(e);
}
/**
* Closes all {@code Closeable} instances that have been added to this
* {@code Closer}. If an exception was thrown in the try block and passed to one
* of the {@code exceptionThrown} methods, any exceptions thrown when attempting
* to close a closeable will be suppressed. Otherwise, the <i>first</i>
* exception to be thrown from an attempt to close a closeable will be thrown
* and any additional exceptions that are thrown after that will be suppressed.
*/
@Override
public void close() throws IOException {
Throwable throwable = thrown;
// close closeables in LIFO order
while (!stack.isEmpty()) {
Closeable closeable = stack.removeFirst();
try {
closeable.close();
} catch (Throwable e) {
if (throwable == null) {
throwable = e;
} else {
suppressor.suppress(closeable, throwable, e);
}
}
}
if (thrown == null && throwable != null) {
Throwables.propagateIfPossible(throwable, IOException.class);
throw new AssertionError(throwable); // not possible
}
}
/**
* Suppression strategy interface.
*/
@VisibleForTesting
interface Suppressor {
/**
* Suppresses the given exception ({@code suppressed}) which was thrown when
* attempting to close the given closeable. {@code thrown} is the exception that
* is actually being thrown from the method. Implementations of this method
* should not throw under any circumstances.
*/
void suppress(Closeable closeable, Throwable thrown, Throwable suppressed);
}
/**
* Suppresses exceptions by logging them.
*/
@VisibleForTesting
static final class LoggingSuppressor implements Suppressor {
static final LoggingSuppressor INSTANCE = new LoggingSuppressor();
@Override
public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) {
// log to the same place as Closeables
Closeables.logger.log(Level.WARNING, "Suppressing exception thrown when closing " + closeable, suppressed);
}
}
/**
* Suppresses exceptions by adding them to the exception that will be thrown
* using JDK7's addSuppressed(Throwable) mechanism.
*/
@VisibleForTesting
static final class SuppressingSuppressor implements Suppressor {
static final SuppressingSuppressor INSTANCE = new SuppressingSuppressor();
static boolean isAvailable() {
return addSuppressed != null;
}
static final Method addSuppressed = getAddSuppressed();
private static Method getAddSuppressed() {
try {
return Throwable.class.getMethod("addSuppressed", Throwable.class);
} catch (Throwable e) {
return null;
}
}
@Override
public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) {
// ensure no exceptions from addSuppressed
if (thrown == suppressed) {
return;
}
try {
addSuppressed.invoke(thrown, suppressed);
} catch (Throwable e) {
// if, somehow, IllegalAccessException or another exception is thrown, fall back
// to logging
LoggingSuppressor.INSTANCE.suppress(closeable, thrown, suppressed);
}
}
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2007 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 java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nullable;
import com.google.common.annotations.Beta;
/**
* An {@link InputStream} that counts the number of bytes read.
*
* @author Chris Nokleberg
* @since 1.0
*/
@Beta
public final class CountingInputStream extends FilterInputStream {
private long count;
private long mark = -1;
/**
* Wraps another input stream, counting the number of bytes read.
*
* @param in the input stream to be wrapped
*/
public CountingInputStream(@Nullable InputStream in) {
super(in);
}
/** Returns the number of bytes read. */
public long getCount() {
return count;
}
@Override
public int read() throws IOException {
int result = in.read();
if (result != -1) {
count++;
}
return result;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int result = in.read(b, off, len);
if (result != -1) {
count += result;
}
return result;
}
@Override
public long skip(long n) throws IOException {
long result = in.skip(n);
count += result;
return result;
}
@Override
public synchronized void mark(int readlimit) {
in.mark(readlimit);
mark = count;
// it's okay to mark even if mark isn't supported, as reset won't work
}
@Override
public synchronized void reset() throws IOException {
if (!in.markSupported()) {
throw new IOException("Mark not supported");
}
if (mark == -1) {
throw new IOException("Mark not set");
}
in.reset();
count = mark;
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (C) 2007 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 java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.annotation.Nullable;
import com.google.common.annotations.Beta;
/**
* An OutputStream that counts the number of bytes written.
*
* @author Chris Nokleberg
* @since 1.0
*/
@Beta
public final class CountingOutputStream extends FilterOutputStream {
private long count;
/**
* Wraps another output stream, counting the number of bytes written.
*
* @param out the output stream to be wrapped
*/
public CountingOutputStream(@Nullable OutputStream out) {
super(out);
}
/** Returns the number of bytes written. */
public long getCount() {
return count;
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
count += len;
}
@Override
public void write(int b) throws IOException {
out.write(b);
count++;
}
// Overriding close() because FilterOutputStream's close() method pre-JDK8 has
// bad behavior:
// it silently ignores any exception thrown by flush(). Instead, just close the
// delegate stream.
// It should flush itself if necessary.
@Override
public void close() throws IOException {
out.close();
}
}

View File

@ -0,0 +1,31 @@
/*
* 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;
/**
* Modes for opening a file for writing. The default when mode when none is
* specified is to truncate the file before writing.
*
* @author Colin Decker
*/
public enum FileWriteMode {
/**
* Specifies that writes to the opened file should append to the end of the
* file.
*/
APPEND
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2007 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 java.io.Flushable;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.google.common.annotations.Beta;
/**
* Utility methods for working with {@link Flushable} objects.
*
* @author Michael Lancaster
* @since 1.0
*/
@Beta
public final class Flushables {
private static final Logger logger = Logger.getLogger(Flushables.class.getName());
private Flushables() {
}
/**
* Flush a {@link Flushable}, with control over whether an {@code IOException}
* may be thrown.
*
* <p>
* If {@code swallowIOException} is true, then we don't rethrow
* {@code IOException}, but merely log it.
*
* @param flushable the {@code Flushable} object to be flushed.
* @param swallowIOException if true, don't propagate IO exceptions thrown by
* the {@code flush} method
* @throws IOException if {@code swallowIOException} is false and
* {@link Flushable#flush} throws an {@code IOException}.
* @see Closeables#close
*/
public static void flush(Flushable flushable, boolean swallowIOException) throws IOException {
try {
flushable.flush();
} catch (IOException e) {
if (swallowIOException) {
logger.log(Level.WARNING, "IOException thrown while flushing Flushable.", e);
} else {
throw e;
}
}
}
/**
* Equivalent to calling {@code flush(flushable, true)}, but with no
* {@code IOException} in the signature.
*
* @param flushable the {@code Flushable} object to be flushed.
*/
public static void flushQuietly(Flushable flushable) {
try {
flush(flushable, true);
} catch (IOException e) {
logger.log(Level.SEVERE, "IOException should not have been thrown.", e);
}
}
}

View File

@ -0,0 +1,241 @@
/*
* 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 static com.google.common.base.Preconditions.checkPositionIndexes;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
/**
* Provides simple GWT-compatible substitutes for {@code InputStream},
* {@code OutputStream}, {@code Reader}, and {@code Writer} so that
* {@code BaseEncoding} can use streaming implementations while remaining
* GWT-compatible.
*
* @author Louis Wasserman
*/
@GwtCompatible(emulated = true)
final class GwtWorkarounds {
private GwtWorkarounds() {
}
/**
* A GWT-compatible substitute for a {@code Reader}.
*/
interface CharInput {
int read() throws IOException;
void close() throws IOException;
}
/**
* Views a {@code Reader} as a {@code CharInput}.
*/
@GwtIncompatible("Reader")
static CharInput asCharInput(final Reader reader) {
checkNotNull(reader);
return new CharInput() {
@Override
public int read() throws IOException {
return reader.read();
}
@Override
public void close() throws IOException {
reader.close();
}
};
}
/**
* Views a {@code CharSequence} as a {@code CharInput}.
*/
static CharInput asCharInput(final CharSequence chars) {
checkNotNull(chars);
return new CharInput() {
int index = 0;
@Override
public int read() {
if (index < chars.length()) {
return chars.charAt(index++);
} else {
return -1;
}
}
@Override
public void close() {
index = chars.length();
}
};
}
/**
* A GWT-compatible substitute for an {@code InputStream}.
*/
interface ByteInput {
int read() throws IOException;
void close() throws IOException;
}
/**
* Views a {@code ByteInput} as an {@code InputStream}.
*/
@GwtIncompatible("InputStream")
static InputStream asInputStream(final ByteInput input) {
checkNotNull(input);
return new InputStream() {
@Override
public int read() throws IOException {
return input.read();
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
checkNotNull(b);
checkPositionIndexes(off, off + len, b.length);
if (len == 0) {
return 0;
}
int firstByte = read();
if (firstByte == -1) {
return -1;
}
b[off] = (byte) firstByte;
for (int dst = 1; dst < len; dst++) {
int readByte = read();
if (readByte == -1) {
return dst;
}
b[off + dst] = (byte) readByte;
}
return len;
}
@Override
public void close() throws IOException {
input.close();
}
};
}
/**
* A GWT-compatible substitute for an {@code OutputStream}.
*/
interface ByteOutput {
void write(byte b) throws IOException;
void flush() throws IOException;
void close() throws IOException;
}
/**
* Views a {@code ByteOutput} as an {@code OutputStream}.
*/
@GwtIncompatible("OutputStream")
static OutputStream asOutputStream(final ByteOutput output) {
checkNotNull(output);
return new OutputStream() {
@Override
public void write(int b) throws IOException {
output.write((byte) b);
}
@Override
public void flush() throws IOException {
output.flush();
}
@Override
public void close() throws IOException {
output.close();
}
};
}
/**
* A GWT-compatible substitute for a {@code Writer}.
*/
interface CharOutput {
void write(char c) throws IOException;
void flush() throws IOException;
void close() throws IOException;
}
/**
* Views a {@code Writer} as a {@code CharOutput}.
*/
@GwtIncompatible("Writer")
static CharOutput asCharOutput(final Writer writer) {
checkNotNull(writer);
return new CharOutput() {
@Override
public void write(char c) throws IOException {
writer.append(c);
}
@Override
public void flush() throws IOException {
writer.flush();
}
@Override
public void close() throws IOException {
writer.close();
}
};
}
/**
* Returns a {@code CharOutput} whose {@code toString()} method can be used to
* get the combined output.
*/
static CharOutput stringBuilderOutput(int initialSize) {
final StringBuilder builder = new StringBuilder(initialSize);
return new CharOutput() {
@Override
public void write(char c) {
builder.append(c);
}
@Override
public void flush() {
}
@Override
public void close() {
}
@Override
public String toString() {
return builder.toString();
}
};
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2007 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 java.io.IOException;
/**
* A factory for readable streams of bytes or characters.
*
* @author Chris Nokleberg
* @since 1.0
* @deprecated For {@code InputSupplier<? extends InputStream>}, use
* {@link ByteSource} instead. For
* {@code InputSupplier<? extends Reader>}, use {@link CharSource}.
* Implementations of {@code InputSupplier} that don't fall into one
* of those categories do not benefit from any of the methods in
* {@code common.io} and should use a different interface. This
* interface is scheduled for removal in June 2015.
*/
@Deprecated
public interface InputSupplier<T> {
/**
* Returns an object that encapsulates a readable resource.
* <p>
* Like {@link Iterable#iterator}, this method may be called repeatedly to get
* independent channels to the same underlying resource.
* <p>
* Where the channel maintains a position within the resource, moving that
* cursor within one channel should not affect the starting position of channels
* returned by other calls.
*/
T getInput() throws IOException;
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (C) 2007 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 java.io.IOException;
/**
* Package-protected abstract class that implements the line reading algorithm
* used by {@link LineReader}. Line separators are per
* {@link java.io.BufferedReader}: line feed, carriage return, or carriage
* return followed immediately by a linefeed.
*
* <p>
* Subclasses must implement {@link #handleLine}, call {@link #add} to pass
* character data, and call {@link #finish} at the end of stream.
*
* @author Chris Nokleberg
* @since 1.0
*/
abstract class LineBuffer {
/** Holds partial line contents. */
private StringBuilder line = new StringBuilder();
/** Whether a line ending with a CR is pending processing. */
private boolean sawReturn;
/**
* Process additional characters from the stream. When a line separator is found
* the contents of the line and the line separator itself are passed to the
* abstract {@link #handleLine} method.
*
* @param cbuf the character buffer to process
* @param off the offset into the buffer
* @param len the number of characters to process
* @throws IOException if an I/O error occurs
* @see #finish
*/
protected void add(char[] cbuf, int off, int len) throws IOException {
int pos = off;
if (sawReturn && len > 0) {
// Last call to add ended with a CR; we can handle the line now.
if (finishLine(cbuf[pos] == '\n')) {
pos++;
}
}
int start = pos;
for (int end = off + len; pos < end; pos++) {
switch (cbuf[pos]) {
case '\r':
line.append(cbuf, start, pos - start);
sawReturn = true;
if (pos + 1 < end) {
if (finishLine(cbuf[pos + 1] == '\n')) {
pos++;
}
}
start = pos + 1;
break;
case '\n':
line.append(cbuf, start, pos - start);
finishLine(true);
start = pos + 1;
break;
default:
// do nothing
}
}
line.append(cbuf, start, off + len - start);
}
/** Called when a line is complete. */
private boolean finishLine(boolean sawNewline) throws IOException {
handleLine(line.toString(), sawReturn ? (sawNewline ? "\r\n" : "\r") : (sawNewline ? "\n" : ""));
line = new StringBuilder();
sawReturn = false;
return sawNewline;
}
/**
* Subclasses must call this method after finishing character processing, in
* order to ensure that any unterminated line in the buffer is passed to
* {@link #handleLine}.
*
* @throws IOException if an I/O error occurs
*/
protected void finish() throws IOException {
if (sawReturn || line.length() > 0) {
finishLine(false);
}
}
/**
* Called for each line found in the character data passed to {@link #add}.
*
* @param line a line of text (possibly empty), without any line separators
* @param end the line separator; one of {@code "\r"}, {@code "\n"},
* {@code "\r\n"}, or {@code ""}
* @throws IOException if an I/O error occurs
*/
protected abstract void handleLine(String line, String end) throws IOException;
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2009 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 java.io.IOException;
import com.google.common.annotations.Beta;
/**
* A callback to be used with the streaming {@code readLines} methods.
*
* <p>
* {@link #processLine} will be called for each line that is read, and should
* return {@code false} when you want to stop processing.
*
* @author Miles Barr
* @since 1.0
*/
@Beta
public interface LineProcessor<T> {
/**
* This method will be called once for each line.
*
* @param line the line read from the input, without delimiter
* @return true to continue processing, false to stop
*/
boolean processLine(String line) throws IOException;
/** Return the result of processing all the lines. */
T getResult();
}

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2007 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.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
import java.util.LinkedList;
import java.util.Queue;
import com.google.common.annotations.Beta;
/**
* A class for reading lines of text. Provides the same functionality as
* {@link java.io.BufferedReader#readLine()} but for all {@link Readable}
* objects, not just instances of {@link Reader}.
*
* @author Chris Nokleberg
* @since 1.0
*/
@Beta
public final class LineReader {
private final Readable readable;
private final Reader reader;
private final char[] buf = new char[0x1000]; // 4K
private final CharBuffer cbuf = CharBuffer.wrap(buf);
private final Queue<String> lines = new LinkedList<String>();
private final LineBuffer lineBuf = new LineBuffer() {
@Override
protected void handleLine(String line, String end) {
lines.add(line);
}
};
/**
* Creates a new instance that will read lines from the given {@code Readable}
* object.
*/
public LineReader(Readable readable) {
this.readable = checkNotNull(readable);
this.reader = (readable instanceof Reader) ? (Reader) readable : null;
}
/**
* Reads a line of text. A line is considered to be terminated by any one of a
* line feed ({@code '\n'}), a carriage return ({@code '\r'}), or a carriage
* return followed immediately by a linefeed ({@code "\r\n"}).
*
* @return a {@code String} containing the contents of the line, not including
* any line-termination characters, or {@code null} if the end of the
* stream has been reached.
* @throws IOException if an I/O error occurs
*/
public String readLine() throws IOException {
while (lines.peek() == null) {
cbuf.clear();
// The default implementation of Reader#read(CharBuffer) allocates a
// temporary char[], so we call Reader#read(char[], int, int) instead.
int read = (reader != null) ? reader.read(buf, 0, buf.length) : readable.read(cbuf);
if (read == -1) {
lineBuf.finish();
break;
}
lineBuf.add(buf, 0, read);
}
return lines.poll();
}
}

View File

@ -0,0 +1,230 @@
/*
* Copyright (C) 2007 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 java.io.DataInput;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
/**
* An implementation of {@link DataInput} that uses little-endian byte ordering
* for reading {@code short}, {@code int}, {@code float}, {@code double}, and
* {@code long} values.
* <p>
* <b>Note:</b> This class intentionally violates the specification of its
* supertype {@code DataInput}, which explicitly requires big-endian byte order.
*
* @author Chris Nokleberg
* @author Keith Bottner
* @since 8.0
*/
@Beta
public final class LittleEndianDataInputStream extends FilterInputStream implements DataInput {
/**
* Creates a {@code LittleEndianDataInputStream} that wraps the given stream.
*
* @param in the stream to delegate to
*/
public LittleEndianDataInputStream(InputStream in) {
super(Preconditions.checkNotNull(in));
}
/**
* This method will throw an {@link UnsupportedOperationException}.
*/
@Override
public String readLine() {
throw new UnsupportedOperationException("readLine is not supported");
}
@Override
public void readFully(byte[] b) throws IOException {
ByteStreams.readFully(this, b);
}
@Override
public void readFully(byte[] b, int off, int len) throws IOException {
ByteStreams.readFully(this, b, off, len);
}
@Override
public int skipBytes(int n) throws IOException {
return (int) in.skip(n);
}
@Override
public int readUnsignedByte() throws IOException {
int b1 = in.read();
if (0 > b1) {
throw new EOFException();
}
return b1;
}
/**
* Reads an unsigned {@code short} as specified by
* {@link DataInputStream#readUnsignedShort()}, except using little-endian byte
* order.
*
* @return the next two bytes of the input stream, interpreted as an unsigned
* 16-bit integer in little-endian byte order
* @throws IOException if an I/O error occurs
*/
@Override
public int readUnsignedShort() throws IOException {
byte b1 = readAndCheckByte();
byte b2 = readAndCheckByte();
return Ints.fromBytes((byte) 0, (byte) 0, b2, b1);
}
/**
* Reads an integer as specified by {@link DataInputStream#readInt()}, except
* using little-endian byte order.
*
* @return the next four bytes of the input stream, interpreted as an
* {@code int} in little-endian byte order
* @throws IOException if an I/O error occurs
*/
@Override
public int readInt() throws IOException {
byte b1 = readAndCheckByte();
byte b2 = readAndCheckByte();
byte b3 = readAndCheckByte();
byte b4 = readAndCheckByte();
return Ints.fromBytes(b4, b3, b2, b1);
}
/**
* Reads a {@code long} as specified by {@link DataInputStream#readLong()},
* except using little-endian byte order.
*
* @return the next eight bytes of the input stream, interpreted as a
* {@code long} in little-endian byte order
* @throws IOException if an I/O error occurs
*/
@Override
public long readLong() throws IOException {
byte b1 = readAndCheckByte();
byte b2 = readAndCheckByte();
byte b3 = readAndCheckByte();
byte b4 = readAndCheckByte();
byte b5 = readAndCheckByte();
byte b6 = readAndCheckByte();
byte b7 = readAndCheckByte();
byte b8 = readAndCheckByte();
return Longs.fromBytes(b8, b7, b6, b5, b4, b3, b2, b1);
}
/**
* Reads a {@code float} as specified by {@link DataInputStream#readFloat()},
* except using little-endian byte order.
*
* @return the next four bytes of the input stream, interpreted as a
* {@code float} in little-endian byte order
* @throws IOException if an I/O error occurs
*/
@Override
public float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
/**
* Reads a {@code double} as specified by {@link DataInputStream#readDouble()},
* except using little-endian byte order.
*
* @return the next eight bytes of the input stream, interpreted as a
* {@code double} in little-endian byte order
* @throws IOException if an I/O error occurs
*/
@Override
public double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
@Override
public String readUTF() throws IOException {
return new DataInputStream(in).readUTF();
}
/**
* Reads a {@code short} as specified by {@link DataInputStream#readShort()},
* except using little-endian byte order.
*
* @return the next two bytes of the input stream, interpreted as a
* {@code short} in little-endian byte order.
* @throws IOException if an I/O error occurs.
*/
@Override
public short readShort() throws IOException {
return (short) readUnsignedShort();
}
/**
* Reads a char as specified by {@link DataInputStream#readChar()}, except using
* little-endian byte order.
*
* @return the next two bytes of the input stream, interpreted as a {@code char}
* in little-endian byte order
* @throws IOException if an I/O error occurs
*/
@Override
public char readChar() throws IOException {
return (char) readUnsignedShort();
}
@Override
public byte readByte() throws IOException {
return (byte) readUnsignedByte();
}
@Override
public boolean readBoolean() throws IOException {
return readUnsignedByte() != 0;
}
/**
* Reads a byte from the input stream checking that the end of file (EOF) has
* not been encountered.
*
* @return byte read from input
* @throws IOException if an error is encountered while reading
* @throws EOFException if the end of file (EOF) is encountered.
*/
private byte readAndCheckByte() throws IOException, EOFException {
int b1 = in.read();
if (-1 == b1) {
throw new EOFException();
}
return (byte) b1;
}
}

View File

@ -0,0 +1,184 @@
/*
* Copyright (C) 2007 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 java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Longs;
/**
* An implementation of {@link DataOutput} that uses little-endian byte ordering
* for writing {@code char}, {@code short}, {@code int}, {@code float}, {@code
* double}, and {@code long} values.
* <p>
* <b>Note:</b> This class intentionally violates the specification of its
* supertype {@code DataOutput}, which explicitly requires big-endian byte
* order.
*
* @author Chris Nokleberg
* @author Keith Bottner
* @since 8.0
*/
@Beta
public class LittleEndianDataOutputStream extends FilterOutputStream implements DataOutput {
/**
* Creates a {@code LittleEndianDataOutputStream} that wraps the given stream.
*
* @param out the stream to delegate to
*/
public LittleEndianDataOutputStream(OutputStream out) {
super(new DataOutputStream(Preconditions.checkNotNull(out)));
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
// Override slow FilterOutputStream impl
out.write(b, off, len);
}
@Override
public void writeBoolean(boolean v) throws IOException {
((DataOutputStream) out).writeBoolean(v);
}
@Override
public void writeByte(int v) throws IOException {
((DataOutputStream) out).writeByte(v);
}
/**
* @deprecated The semantics of {@code writeBytes(String s)} are considered
* dangerous. Please use {@link #writeUTF(String s)},
* {@link #writeChars(String s)} or another write method instead.
*/
@Deprecated
@Override
public void writeBytes(String s) throws IOException {
((DataOutputStream) out).writeBytes(s);
}
/**
* Writes a char as specified by {@link DataOutputStream#writeChar(int)}, except
* using little-endian byte order.
*
* @throws IOException if an I/O error occurs
*/
@Override
public void writeChar(int v) throws IOException {
writeShort(v);
}
/**
* Writes a {@code String} as specified by
* {@link DataOutputStream#writeChars(String)}, except each character is written
* using little-endian byte order.
*
* @throws IOException if an I/O error occurs
*/
@Override
public void writeChars(String s) throws IOException {
for (int i = 0; i < s.length(); i++) {
writeChar(s.charAt(i));
}
}
/**
* Writes a {@code double} as specified by
* {@link DataOutputStream#writeDouble(double)}, except using little-endian byte
* order.
*
* @throws IOException if an I/O error occurs
*/
@Override
public void writeDouble(double v) throws IOException {
writeLong(Double.doubleToLongBits(v));
}
/**
* Writes a {@code float} as specified by
* {@link DataOutputStream#writeFloat(float)}, except using little-endian byte
* order.
*
* @throws IOException if an I/O error occurs
*/
@Override
public void writeFloat(float v) throws IOException {
writeInt(Float.floatToIntBits(v));
}
/**
* Writes an {@code int} as specified by {@link DataOutputStream#writeInt(int)},
* except using little-endian byte order.
*
* @throws IOException if an I/O error occurs
*/
@Override
public void writeInt(int v) throws IOException {
out.write(0xFF & v);
out.write(0xFF & (v >> 8));
out.write(0xFF & (v >> 16));
out.write(0xFF & (v >> 24));
}
/**
* Writes a {@code long} as specified by
* {@link DataOutputStream#writeLong(long)}, except using little-endian byte
* order.
*
* @throws IOException if an I/O error occurs
*/
@Override
public void writeLong(long v) throws IOException {
byte[] bytes = Longs.toByteArray(Long.reverseBytes(v));
write(bytes, 0, bytes.length);
}
/**
* Writes a {@code short} as specified by
* {@link DataOutputStream#writeShort(int)}, except using little-endian byte
* order.
*
* @throws IOException if an I/O error occurs
*/
@Override
public void writeShort(int v) throws IOException {
out.write(0xFF & v);
out.write(0xFF & (v >> 8));
}
@Override
public void writeUTF(String str) throws IOException {
((DataOutputStream) out).writeUTF(str);
}
// Overriding close() because FilterOutputStream's close() method pre-JDK8 has
// bad behavior:
// it silently ignores any exception thrown by flush(). Instead, just close the
// delegate stream.
// It should flush itself if necessary.
@Override
public void close() throws IOException {
out.close();
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright (C) 2007 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.IOException;
import java.io.InputStream;
import java.util.Iterator;
import javax.annotation.Nullable;
/**
* An {@link InputStream} that concatenates multiple substreams. At most one
* stream will be open at a time.
*
* @author Chris Nokleberg
* @since 1.0
*/
final class MultiInputStream extends InputStream {
private Iterator<? extends ByteSource> it;
private InputStream in;
/**
* Creates a new instance.
*
* @param it an iterator of I/O suppliers that will provide each substream
*/
public MultiInputStream(Iterator<? extends ByteSource> it) throws IOException {
this.it = checkNotNull(it);
advance();
}
@Override
public void close() throws IOException {
if (in != null) {
try {
in.close();
} finally {
in = null;
}
}
}
/**
* Closes the current input stream and opens the next one, if any.
*/
private void advance() throws IOException {
close();
if (it.hasNext()) {
in = it.next().openStream();
}
}
@Override
public int available() throws IOException {
if (in == null) {
return 0;
}
return in.available();
}
@Override
public boolean markSupported() {
return false;
}
@Override
public int read() throws IOException {
if (in == null) {
return -1;
}
int result = in.read();
if (result == -1) {
advance();
return read();
}
return result;
}
@Override
public int read(@Nullable byte[] b, int off, int len) throws IOException {
if (in == null) {
return -1;
}
int result = in.read(b, off, len);
if (result == -1) {
advance();
return read(b, off, len);
}
return result;
}
@Override
public long skip(long n) throws IOException {
if (in == null || n <= 0) {
return 0;
}
long result = in.skip(n);
if (result != 0) {
return result;
}
if (read() == -1) {
return 0;
}
return 1 + in.skip(n - 1);
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2008 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 java.io.IOException;
import java.io.Reader;
import java.util.Iterator;
import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
/**
* A {@link Reader} that concatenates multiple readers.
*
* @author Bin Zhu
* @since 1.0
*/
class MultiReader extends Reader {
private final Iterator<? extends CharSource> it;
private Reader current;
MultiReader(Iterator<? extends CharSource> readers) throws IOException {
this.it = readers;
advance();
}
/**
* Closes the current reader and opens the next one, if any.
*/
private void advance() throws IOException {
close();
if (it.hasNext()) {
current = it.next().openStream();
}
}
@Override
public int read(@Nullable char cbuf[], int off, int len) throws IOException {
if (current == null) {
return -1;
}
int result = current.read(cbuf, off, len);
if (result == -1) {
advance();
return read(cbuf, off, len);
}
return result;
}
@Override
public long skip(long n) throws IOException {
Preconditions.checkArgument(n >= 0, "n is negative");
if (n > 0) {
while (current != null) {
long result = current.skip(n);
if (result > 0) {
return result;
}
advance();
}
}
return 0;
}
@Override
public boolean ready() throws IOException {
return (current != null) && current.ready();
}
@Override
public void close() throws IOException {
if (current != null) {
try {
current.close();
} finally {
current = null;
}
}
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2007 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 java.io.IOException;
/**
* A factory for writable streams of bytes or characters.
*
* @author Chris Nokleberg
* @since 1.0
* @deprecated For {@code OutputSupplier<? extends OutputStream>}, use
* {@link ByteSink} instead. For
* {@code OutputSupplier<? extends Writer>}, use {@link CharSink}.
* Implementations of {@code OutputSupplier} that don't fall into
* one of those categories do not benefit from any of the methods in
* {@code common.io} and should use a different interface. This
* interface is scheduled for removal in June 2015.
*/
@Deprecated
public interface OutputSupplier<T> {
/**
* Returns an object that encapsulates a writable resource.
* <p>
* Like {@link Iterable#iterator}, this method may be called repeatedly to get
* independent channels to the same underlying resource.
* <p>
* Where the channel maintains a position within the resource, moving that
* cursor within one channel should not affect the starting position of channels
* returned by other calls.
*/
T getOutput() throws IOException;
}

View File

@ -0,0 +1,241 @@
/*
* Copyright (C) 2007 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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.List;
import com.google.common.annotations.Beta;
import com.google.common.base.Charsets;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
/**
* Provides utility methods for working with resources in the classpath. Note
* that even though these methods use {@link URL} parameters, they are usually
* not appropriate for HTTP or other non-classpath resources.
*
* <p>
* All method parameters must be non-null unless documented otherwise.
*
* @author Chris Nokleberg
* @author Ben Yu
* @author Colin Decker
* @since 1.0
*/
@Beta
public final class Resources {
private Resources() {
}
/**
* Returns a factory that will supply instances of {@link InputStream} that read
* from the given URL.
*
* @param url the URL to read from
* @return the factory
* @deprecated Use {@link #asByteSource(URL)} instead. This method is scheduled
* for removal in Guava 18.0.
*/
@Deprecated
public static InputSupplier<InputStream> newInputStreamSupplier(URL url) {
return ByteStreams.asInputSupplier(asByteSource(url));
}
/**
* Returns a {@link ByteSource} that reads from the given URL.
*
* @since 14.0
*/
public static ByteSource asByteSource(URL url) {
return new UrlByteSource(url);
}
/**
* A byte source that reads from a URL using {@link URL#openStream()}.
*/
private static final class UrlByteSource extends ByteSource {
private final URL url;
private UrlByteSource(URL url) {
this.url = checkNotNull(url);
}
@Override
public InputStream openStream() throws IOException {
return url.openStream();
}
@Override
public String toString() {
return "Resources.asByteSource(" + url + ")";
}
}
/**
* Returns a factory that will supply instances of {@link InputStreamReader}
* that read a URL using the given character set.
*
* @param url the URL to read from
* @param charset the charset used to decode the input stream; see
* {@link Charsets} for helpful predefined constants
* @return the factory
* @deprecated Use {@link #asCharSource(URL, Charset)} instead. This method is
* scheduled for removal in Guava 18.0.
*/
@Deprecated
public static InputSupplier<InputStreamReader> newReaderSupplier(URL url, Charset charset) {
return CharStreams.asInputSupplier(asCharSource(url, charset));
}
/**
* Returns a {@link CharSource} that reads from the given URL using the given
* character set.
*
* @since 14.0
*/
public static CharSource asCharSource(URL url, Charset charset) {
return asByteSource(url).asCharSource(charset);
}
/**
* Reads all bytes from a URL into a byte array.
*
* @param url the URL to read from
* @return a byte array containing all the bytes from the URL
* @throws IOException if an I/O error occurs
*/
public static byte[] toByteArray(URL url) throws IOException {
return asByteSource(url).read();
}
/**
* Reads all characters from a URL into a {@link String}, using the given
* character set.
*
* @param url the URL to read from
* @param charset the charset used to decode the input stream; see
* {@link Charsets} for helpful predefined constants
* @return a string containing all the characters from the URL
* @throws IOException if an I/O error occurs.
*/
public static String toString(URL url, Charset charset) throws IOException {
return asCharSource(url, charset).read();
}
/**
* Streams lines from a URL, stopping when our callback returns false, or we
* have read all of the lines.
*
* @param url the URL to read from
* @param charset the charset used to decode the input stream; see
* {@link Charsets} for helpful predefined constants
* @param callback the LineProcessor to use to handle the lines
* @return the output of processing the lines
* @throws IOException if an I/O error occurs
*/
public static <T> T readLines(URL url, Charset charset, LineProcessor<T> callback) throws IOException {
return CharStreams.readLines(newReaderSupplier(url, charset), callback);
}
/**
* Reads all of the lines from a URL. The lines do not include line-termination
* characters, but do include other leading and trailing whitespace.
*
* <p>
* This method returns a mutable {@code List}. For an {@code ImmutableList}, use
* {@code Resources.asCharSource(url, charset).readLines()}.
*
* @param url the URL to read from
* @param charset the charset used to decode the input stream; see
* {@link Charsets} for helpful predefined constants
* @return a mutable {@link List} containing all the lines
* @throws IOException if an I/O error occurs
*/
public static List<String> readLines(URL url, Charset charset) throws IOException {
// don't use asCharSource(url, charset).readLines() because that returns
// an immutable list, which would change the behavior of this method
return readLines(url, charset, new LineProcessor<List<String>>() {
final List<String> result = Lists.newArrayList();
@Override
public boolean processLine(String line) {
result.add(line);
return true;
}
@Override
public List<String> getResult() {
return result;
}
});
}
/**
* Copies all bytes from a URL to an output stream.
*
* @param from the URL to read from
* @param to the output stream
* @throws IOException if an I/O error occurs
*/
public static void copy(URL from, OutputStream to) throws IOException {
asByteSource(from).copyTo(to);
}
/**
* Returns a {@code URL} pointing to {@code resourceName} if the resource is
* found using the {@linkplain Thread#getContextClassLoader() context class
* loader}. In simple environments, the context class loader will find resources
* from the class path. In environments where different threads can have
* different class loaders, for example app servers, the context class loader
* will typically have been set to an appropriate loader for the current thread.
*
* <p>
* In the unusual case where the context class loader is null, the class loader
* that loaded this class ({@code Resources}) will be used instead.
*
* @throws IllegalArgumentException if the resource is not found
*/
public static URL getResource(String resourceName) {
ClassLoader loader = Objects.firstNonNull(Thread.currentThread().getContextClassLoader(),
Resources.class.getClassLoader());
URL url = loader.getResource(resourceName);
checkArgument(url != null, "resource %s not found.", resourceName);
return url;
}
/**
* Given a {@code resourceName} that is relative to {@code contextClass},
* returns a {@code URL} pointing to the named resource.
*
* @throws IllegalArgumentException if the resource is not found
*/
public static URL getResource(Class<?> contextClass, String resourceName) {
URL url = contextClass.getResource(resourceName);
checkArgument(url != null, "resource %s relative to %s not found.", resourceName, contextClass.getName());
return url;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2007 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.
*/
/**
* This package contains utility methods and classes for working with Java I/O;
* for example input streams, output streams, readers, writers, and files.
*
* <p>
* At the core of this package are the Source/Sink types:
* {@link com.google.common.io.ByteSource ByteSource},
* {@link com.google.common.io.CharSource CharSource},
* {@link com.google.common.io.ByteSink ByteSink} and
* {@link com.google.common.io.CharSink CharSink}. They are factories for I/O
* streams that provide many convenience methods that handle both opening and
* closing streams for you.
*
* <p>
* This package is a part of the open-source
* <a href="http://guava-libraries.googlecode.com">Guava libraries</a>. For more
* information on Sources and Sinks as well as other features of this package,
* see <a href="https://code.google.com/p/guava-libraries/wiki/IOExplained">I/O
* Explained</a> on the Guava wiki.
*
* @author Chris Nokleberg
*/
@ParametersAreNonnullByDefault
package com.google.common.io;
import javax.annotation.ParametersAreNonnullByDefault;