diff options
Diffstat (limited to 'guava/src/com/google/common/io')
26 files changed, 4269 insertions, 0 deletions
diff --git a/guava/src/com/google/common/io/AppendableWriter.java b/guava/src/com/google/common/io/AppendableWriter.java new file mode 100644 index 0000000..8033e46 --- /dev/null +++ b/guava/src/com/google/common/io/AppendableWriter.java @@ -0,0 +1,117 @@ +/* + * 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 java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.Writer; + +/** + * 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 = 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(String str) throws IOException { + checkNotClosed(); + target.append(str); + } + + @Override public void write(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(CharSequence charSeq) throws IOException { + checkNotClosed(); + target.append(charSeq); + return this; + } + + @Override public Writer append(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."); + } + } +} diff --git a/guava/src/com/google/common/io/ByteArrayDataInput.java b/guava/src/com/google/common/io/ByteArrayDataInput.java new file mode 100644 index 0000000..3f4a467 --- /dev/null +++ b/guava/src/com/google/common/io/ByteArrayDataInput.java @@ -0,0 +1,65 @@ +/* + * 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(); +} diff --git a/guava/src/com/google/common/io/ByteArrayDataOutput.java b/guava/src/com/google/common/io/ByteArrayDataOutput.java new file mode 100644 index 0000000..4d3dd97 --- /dev/null +++ b/guava/src/com/google/common/io/ByteArrayDataOutput.java @@ -0,0 +1,55 @@ +/* + * 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(); +} diff --git a/guava/src/com/google/common/io/ByteProcessor.java b/guava/src/com/google/common/io/ByteProcessor.java new file mode 100644 index 0000000..71953be --- /dev/null +++ b/guava/src/com/google/common/io/ByteProcessor.java @@ -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 com.google.common.annotations.Beta; + +import java.io.IOException; + +/** + * 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(); +} diff --git a/guava/src/com/google/common/io/ByteStreams.java b/guava/src/com/google/common/io/ByteStreams.java new file mode 100644 index 0000000..388504e --- /dev/null +++ b/guava/src/com/google/common/io/ByteStreams.java @@ -0,0 +1,871 @@ +/* + * 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 com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +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 java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; +import java.util.Arrays; +import java.util.zip.Checksum; + +/** + * Provides utility methods for working with byte arrays and I/O streams. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@Beta +public final class ByteStreams { + private static final int BUF_SIZE = 0x1000; // 4K + + private ByteStreams() {} + + /** + * Returns a factory that will supply instances of + * {@link ByteArrayInputStream} that read from the given byte array. + * + * @param b the input buffer + * @return the factory + */ + public static InputSupplier<ByteArrayInputStream> newInputStreamSupplier( + byte[] b) { + return newInputStreamSupplier(b, 0, b.length); + } + + /** + * Returns a factory that will supply instances of + * {@link ByteArrayInputStream} that read from the given byte array. + * + * @param b the input buffer + * @param off the offset in the buffer of the first byte to read + * @param len the maximum number of bytes to read from the buffer + * @return the factory + */ + public static InputSupplier<ByteArrayInputStream> newInputStreamSupplier( + final byte[] b, final int off, final int len) { + return new InputSupplier<ByteArrayInputStream>() { + @Override + public ByteArrayInputStream getInput() { + return new ByteArrayInputStream(b, off, len); + } + }; + } + + /** + * Writes a byte array to an output stream from the given supplier. + * + * @param from the bytes to write + * @param to the output supplier + * @throws IOException if an I/O error occurs + */ + public static void write(byte[] from, + OutputSupplier<? extends OutputStream> to) throws IOException { + Preconditions.checkNotNull(from); + boolean threw = true; + OutputStream out = to.getOutput(); + try { + out.write(from); + threw = false; + } finally { + Closeables.close(out, threw); + } + } + + /** + * Opens input and output streams from the given suppliers, copies all + * bytes from the input to the output, and closes the streams. + * + * @param from the input factory + * @param to the output factory + * @return the number of bytes copied + * @throws IOException if an I/O error occurs + */ + public static long copy(InputSupplier<? extends InputStream> from, + OutputSupplier<? extends OutputStream> to) throws IOException { + int successfulOps = 0; + InputStream in = from.getInput(); + try { + OutputStream out = to.getOutput(); + try { + long count = copy(in, out); + successfulOps++; + return count; + } finally { + Closeables.close(out, successfulOps < 1); + successfulOps++; + } + } finally { + Closeables.close(in, successfulOps < 2); + } + } + + /** + * Opens an input stream from the supplier, copies all bytes from the + * input to the output, and closes the input stream. Does not close + * or flush the output stream. + * + * @param from the input factory + * @param to the output stream to write to + * @return the number of bytes copied + * @throws IOException if an I/O error occurs + */ + public static long copy(InputSupplier<? extends InputStream> from, + OutputStream to) throws IOException { + boolean threw = true; + InputStream in = from.getInput(); + try { + long count = copy(in, to); + threw = false; + return count; + } finally { + Closeables.close(in, threw); + } + } + + /** + * Opens an output stream from the supplier, copies all bytes from the input + * to the output, and closes the output stream. Does not close or flush the + * input stream. + * + * @param from the input stream to read from + * @param to the output factory + * @return the number of bytes copied + * @throws IOException if an I/O error occurs + * @since 10.0 + */ + public static long copy(InputStream from, + OutputSupplier<? extends OutputStream> to) throws IOException { + boolean threw = true; + OutputStream out = to.getOutput(); + try { + long count = copy(from, out); + threw = false; + return count; + } finally { + Closeables.close(out, threw); + } + } + + /** + * Copies all bytes from the input stream to the output stream. + * Does not close or flush either stream. + * + * @param from the input stream to read from + * @param to the output stream to write to + * @return the number of bytes copied + * @throws IOException if an I/O error occurs + */ + public static long copy(InputStream from, OutputStream to) + throws IOException { + byte[] buf = new byte[BUF_SIZE]; + long total = 0; + while (true) { + int r = from.read(buf); + if (r == -1) { + break; + } + to.write(buf, 0, r); + total += r; + } + return total; + } + + /** + * Copies all bytes from the readable channel to the writable channel. + * Does not close or flush either channel. + * + * @param from the readable channel to read from + * @param to the writable channel to write to + * @return the number of bytes copied + * @throws IOException if an I/O error occurs + */ + public static long copy(ReadableByteChannel from, + WritableByteChannel to) throws IOException { + ByteBuffer buf = ByteBuffer.allocate(BUF_SIZE); + long total = 0; + while (from.read(buf) != -1) { + buf.flip(); + while (buf.hasRemaining()) { + total += to.write(buf); + } + buf.clear(); + } + return total; + } + + /** + * Reads all bytes from an input stream into a byte array. + * Does not close the stream. + * + * @param in the input stream to read from + * @return a byte array containing all the bytes from the stream + * @throws IOException if an I/O error occurs + */ + public static byte[] toByteArray(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + copy(in, out); + return out.toByteArray(); + } + + /** + * Returns the data from a {@link InputStream} factory as a byte array. + * + * @param supplier the factory + * @throws IOException if an I/O error occurs + */ + public static byte[] toByteArray( + InputSupplier<? extends InputStream> supplier) throws IOException { + boolean threw = true; + InputStream in = supplier.getInput(); + try { + byte[] result = toByteArray(in); + threw = false; + return result; + } finally { + Closeables.close(in, threw); + } + } + + /** + * Returns a new {@link ByteArrayDataInput} instance to read from the {@code + * bytes} array from the beginning. + */ + public static ByteArrayDataInput newDataInput(byte[] bytes) { + return new ByteArrayDataInputStream(bytes); + } + + /** + * Returns a new {@link ByteArrayDataInput} instance to read from the {@code + * bytes} array, starting at the given position. + * + * @throws IndexOutOfBoundsException if {@code start} is negative or greater + * than the length of the array + */ + public static ByteArrayDataInput newDataInput(byte[] bytes, int start) { + Preconditions.checkPositionIndex(start, bytes.length); + return new ByteArrayDataInputStream(bytes, start); + } + + private static class ByteArrayDataInputStream implements ByteArrayDataInput { + final DataInput input; + + ByteArrayDataInputStream(byte[] bytes) { + this.input = new DataInputStream(new ByteArrayInputStream(bytes)); + } + + ByteArrayDataInputStream(byte[] bytes, int start) { + this.input = new DataInputStream( + new ByteArrayInputStream(bytes, start, bytes.length - start)); + } + + @Override public void readFully(byte b[]) { + try { + input.readFully(b); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public void readFully(byte b[], int off, int len) { + try { + input.readFully(b, off, len); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public int skipBytes(int n) { + try { + return input.skipBytes(n); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public boolean readBoolean() { + try { + return input.readBoolean(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public byte readByte() { + try { + return input.readByte(); + } catch (EOFException e) { + throw new IllegalStateException(e); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public int readUnsignedByte() { + try { + return input.readUnsignedByte(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public short readShort() { + try { + return input.readShort(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public int readUnsignedShort() { + try { + return input.readUnsignedShort(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public char readChar() { + try { + return input.readChar(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public int readInt() { + try { + return input.readInt(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public long readLong() { + try { + return input.readLong(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public float readFloat() { + try { + return input.readFloat(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public double readDouble() { + try { + return input.readDouble(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public String readLine() { + try { + return input.readLine(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override public String readUTF() { + try { + return input.readUTF(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } + + /** + * Returns a new {@link ByteArrayDataOutput} instance with a default size. + */ + public static ByteArrayDataOutput newDataOutput() { + return new ByteArrayDataOutputStream(); + } + + /** + * Returns a new {@link ByteArrayDataOutput} instance sized to hold + * {@code size} bytes before resizing. + * + * @throws IllegalArgumentException if {@code size} is negative + */ + public static ByteArrayDataOutput newDataOutput(int size) { + Preconditions.checkArgument(size >= 0, "Invalid size: %s", size); + return new ByteArrayDataOutputStream(size); + } + + @SuppressWarnings("deprecation") // for writeBytes + private static class ByteArrayDataOutputStream + implements ByteArrayDataOutput { + + final DataOutput output; + final ByteArrayOutputStream byteArrayOutputSteam; + + ByteArrayDataOutputStream() { + this(new ByteArrayOutputStream()); + } + + ByteArrayDataOutputStream(int size) { + this(new ByteArrayOutputStream(size)); + } + + ByteArrayDataOutputStream(ByteArrayOutputStream byteArrayOutputSteam) { + this.byteArrayOutputSteam = byteArrayOutputSteam; + output = new DataOutputStream(byteArrayOutputSteam); + } + + @Override public void write(int b) { + try { + output.write(b); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void write(byte[] b) { + try { + output.write(b); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void write(byte[] b, int off, int len) { + try { + output.write(b, off, len); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void writeBoolean(boolean v) { + try { + output.writeBoolean(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void writeByte(int v) { + try { + output.writeByte(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void writeBytes(String s) { + try { + output.writeBytes(s); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void writeChar(int v) { + try { + output.writeChar(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void writeChars(String s) { + try { + output.writeChars(s); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void writeDouble(double v) { + try { + output.writeDouble(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void writeFloat(float v) { + try { + output.writeFloat(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void writeInt(int v) { + try { + output.writeInt(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void writeLong(long v) { + try { + output.writeLong(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void writeShort(int v) { + try { + output.writeShort(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public void writeUTF(String s) { + try { + output.writeUTF(s); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override public byte[] toByteArray() { + return byteArrayOutputSteam.toByteArray(); + } + + } + + // TODO(chrisn): Not all streams support skipping. + /** Returns the length of a supplied input stream, in bytes. */ + public static long length(InputSupplier<? extends InputStream> supplier) + throws IOException { + long count = 0; + boolean threw = true; + InputStream in = supplier.getInput(); + try { + while (true) { + // We skip only Integer.MAX_VALUE due to JDK overflow bugs. + long amt = in.skip(Integer.MAX_VALUE); + if (amt == 0) { + if (in.read() == -1) { + threw = false; + return count; + } + count++; + } else { + count += amt; + } + } + } finally { + Closeables.close(in, threw); + } + } + + /** + * Returns true if the supplied input streams contain the same bytes. + * + * @throws IOException if an I/O error occurs + */ + public static boolean equal(InputSupplier<? extends InputStream> supplier1, + InputSupplier<? extends InputStream> supplier2) throws IOException { + byte[] buf1 = new byte[BUF_SIZE]; + byte[] buf2 = new byte[BUF_SIZE]; + + boolean threw = true; + InputStream in1 = supplier1.getInput(); + try { + InputStream in2 = supplier2.getInput(); + try { + while (true) { + int read1 = read(in1, buf1, 0, BUF_SIZE); + int read2 = read(in2, buf2, 0, BUF_SIZE); + if (read1 != read2 || !Arrays.equals(buf1, buf2)) { + threw = false; + return false; + } else if (read1 != BUF_SIZE) { + threw = false; + return true; + } + } + } finally { + Closeables.close(in2, threw); + } + } finally { + Closeables.close(in1, threw); + } + } + + /** + * Attempts to read enough bytes from the stream to fill the given byte array, + * with the same behavior as {@link DataInput#readFully(byte[])}. + * Does not close the stream. + * + * @param in the input stream to read from. + * @param b the buffer into which the data is read. + * @throws EOFException if this stream reaches the end before reading all + * the bytes. + * @throws IOException if an I/O error occurs. + */ + public static void readFully(InputStream in, byte[] b) throws IOException { + readFully(in, b, 0, b.length); + } + + /** + * Attempts to read {@code len} bytes from the stream into the given array + * starting at {@code off}, with the same behavior as + * {@link DataInput#readFully(byte[], int, int)}. Does not close the + * stream. + * + * @param in the input stream to read from. + * @param b the buffer into which the data is read. + * @param off an int specifying the offset into the data. + * @param len an int specifying the number of bytes to read. + * @throws EOFException if this stream reaches the end before reading all + * the bytes. + * @throws IOException if an I/O error occurs. + */ + public static void readFully(InputStream in, byte[] b, int off, int len) + throws IOException { + if (read(in, b, off, len) != len) { + throw new EOFException(); + } + } + + /** + * Discards {@code n} bytes of data from the input stream. This method + * will block until the full amount has been skipped. Does not close the + * stream. + * + * @param in the input stream to read from + * @param n the number of bytes to skip + * @throws EOFException if this stream reaches the end before skipping all + * the bytes + * @throws IOException if an I/O error occurs, or the stream does not + * support skipping + */ + public static void skipFully(InputStream in, long n) throws IOException { + while (n > 0) { + long amt = in.skip(n); + if (amt == 0) { + // Force a blocking read to avoid infinite loop + if (in.read() == -1) { + throw new EOFException(); + } + n--; + } else { + n -= amt; + } + } + } + + /** + * Process the bytes of a supplied stream + * + * @param supplier the input stream factory + * @param processor the object to which to pass the bytes of the stream + * @return the result of the byte processor + * @throws IOException if an I/O error occurs + */ + public static <T> T readBytes(InputSupplier<? extends InputStream> supplier, + ByteProcessor<T> processor) throws IOException { + byte[] buf = new byte[BUF_SIZE]; + boolean threw = true; + InputStream in = supplier.getInput(); + try { + int amt; + do { + amt = in.read(buf); + if (amt == -1) { + threw = false; + break; + } + } while (processor.processBytes(buf, 0, amt)); + return processor.getResult(); + } finally { + Closeables.close(in, threw); + } + } + + /** + * Computes and returns the checksum value for a supplied input stream. + * The checksum object is reset when this method returns successfully. + * + * @param supplier the input stream factory + * @param checksum the checksum object + * @return the result of {@link Checksum#getValue} after updating the + * checksum object with all of the bytes in the stream + * @throws IOException if an I/O error occurs + */ + public static long getChecksum(InputSupplier<? extends InputStream> supplier, + final Checksum checksum) throws IOException { + return readBytes(supplier, new ByteProcessor<Long>() { + @Override + public boolean processBytes(byte[] buf, int off, int len) { + checksum.update(buf, off, len); + return true; + } + + @Override + public Long getResult() { + long result = checksum.getValue(); + checksum.reset(); + return result; + } + }); + } + + /** + * Computes the hash code of the data supplied by {@code supplier} using {@code + * hashFunction}. + * + * @param supplier the input stream factory + * @param hashFunction the hash function to use to hash the data + * @return the {@link HashCode} of all of the bytes in the input stream + * @throws IOException if an I/O error occurs + * @since 12.0 + */ + public static HashCode hash( + InputSupplier<? extends InputStream> supplier, HashFunction hashFunction) + throws IOException { + Hasher hasher = hashFunction.newHasher(); + copy(supplier, Funnels.asOutputStream(hasher)); + return hasher.hash(); + } + + /** + * Reads some bytes from an input stream and stores them into the buffer array + * {@code b}. This method blocks until {@code len} bytes of input data have + * been read into the array, or end of file is detected. The number of bytes + * read is returned, possibly zero. Does not close the stream. + * + * <p>A caller can detect EOF if the number of bytes read is less than + * {@code len}. All subsequent calls on the same stream will return zero. + * + * <p>If {@code b} is null, a {@code NullPointerException} is thrown. If + * {@code off} is negative, or {@code len} is negative, or {@code off+len} is + * greater than the length of the array {@code b}, then an + * {@code IndexOutOfBoundsException} is thrown. If {@code len} is zero, then + * no bytes are read. Otherwise, the first byte read is stored into element + * {@code b[off]}, the next one into {@code b[off+1]}, and so on. The number + * of bytes read is, at most, equal to {@code len}. + * + * @param in the input stream to read from + * @param b the buffer into which the data is read + * @param off an int specifying the offset into the data + * @param len an int specifying the number of bytes to read + * @return the number of bytes read + * @throws IOException if an I/O error occurs + */ + public static int read(InputStream in, byte[] b, int off, int len) + throws IOException { + if (len < 0) { + throw new IndexOutOfBoundsException("len is negative"); + } + int total = 0; + while (total < len) { + int result = in.read(b, off + total, len - total); + if (result == -1) { + break; + } + total += result; + } + return total; + } + + /** + * Returns an {@link InputSupplier} that returns input streams from the + * an underlying supplier, where each stream starts at the given + * offset and is limited to the specified number of bytes. + * + * @param supplier the supplier from which to get the raw streams + * @param offset the offset in bytes into the underlying stream where + * the returned streams will start + * @param length the maximum length of the returned streams + * @throws IllegalArgumentException if offset or length are negative + */ + public static InputSupplier<InputStream> slice( + final InputSupplier<? extends InputStream> supplier, + final long offset, + final long length) { + Preconditions.checkNotNull(supplier); + Preconditions.checkArgument(offset >= 0, "offset is negative"); + Preconditions.checkArgument(length >= 0, "length is negative"); + return new InputSupplier<InputStream>() { + @Override public InputStream getInput() throws IOException { + InputStream in = supplier.getInput(); + if (offset > 0) { + try { + skipFully(in, offset); + } catch (IOException e) { + Closeables.closeQuietly(in); + throw e; + } + } + return new LimitInputStream(in, length); + } + }; + } + + /** + * Joins multiple {@link InputStream} suppliers into a single supplier. + * Streams returned from the supplier will contain the concatenated data from + * the streams of the underlying suppliers. + * + * <p>Only one underlying input stream will be open at a time. Closing the + * joined stream will close the open underlying stream. + * + * <p>Reading from the joined stream will throw a {@link NullPointerException} + * if any of the suppliers are null or return null. + * + * @param suppliers the suppliers to concatenate + * @return a supplier that will return a stream containing the concatenated + * stream data + */ + public static InputSupplier<InputStream> join(final + Iterable<? extends InputSupplier<? extends InputStream>> suppliers) { + return new InputSupplier<InputStream>() { + @Override public InputStream getInput() throws IOException { + return new MultiInputStream(suppliers.iterator()); + } + }; + } + + /** Varargs form of {@link #join(Iterable)}. */ + public static InputSupplier<InputStream> join( + InputSupplier<? extends InputStream>... suppliers) { + return join(Arrays.asList(suppliers)); + } +} diff --git a/guava/src/com/google/common/io/CharStreams.java b/guava/src/com/google/common/io/CharStreams.java new file mode 100644 index 0000000..6a70375 --- /dev/null +++ b/guava/src/com/google/common/io/CharStreams.java @@ -0,0 +1,441 @@ +/* + * 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 com.google.common.annotations.Beta; +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; + +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; + +/** + * 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 + * @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 + */ + public static InputSupplier<StringReader> newReaderSupplier( + final String value) { + Preconditions.checkNotNull(value); + return new InputSupplier<StringReader>() { + @Override + public StringReader getInput() { + return new StringReader(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 + */ + public static InputSupplier<InputStreamReader> newReaderSupplier( + final InputSupplier<? extends InputStream> in, final Charset charset) { + Preconditions.checkNotNull(in); + Preconditions.checkNotNull(charset); + return new InputSupplier<InputStreamReader>() { + @Override + public InputStreamReader getInput() throws IOException { + return new InputStreamReader(in.getInput(), 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 + */ + public static OutputSupplier<OutputStreamWriter> newWriterSupplier( + final OutputSupplier<? extends OutputStream> out, final Charset charset) { + Preconditions.checkNotNull(out); + Preconditions.checkNotNull(charset); + return new OutputSupplier<OutputStreamWriter>() { + @Override + public OutputStreamWriter getOutput() throws IOException { + return new OutputStreamWriter(out.getOutput(), 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 + */ + public static <W extends Appendable & Closeable> void write(CharSequence from, + OutputSupplier<W> to) throws IOException { + Preconditions.checkNotNull(from); + boolean threw = true; + W out = to.getOutput(); + try { + out.append(from); + threw = false; + } finally { + Closeables.close(out, threw); + } + } + + /** + * 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 + */ + public static <R extends Readable & Closeable, + W extends Appendable & Closeable> long copy(InputSupplier<R> from, + OutputSupplier<W> to) throws IOException { + int successfulOps = 0; + R in = from.getInput(); + try { + W out = to.getOutput(); + try { + long count = copy(in, out); + successfulOps++; + return count; + } finally { + Closeables.close(out, successfulOps < 1); + successfulOps++; + } + } finally { + Closeables.close(in, successfulOps < 2); + } + } + + /** + * 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 + */ + public static <R extends Readable & Closeable> long copy( + InputSupplier<R> from, Appendable to) throws IOException { + boolean threw = true; + R in = from.getInput(); + try { + long count = copy(in, to); + threw = false; + return count; + } finally { + Closeables.close(in, threw); + } + } + + /** + * 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 { + 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 + */ + public static <R extends Readable & Closeable> String toString( + InputSupplier<R> supplier) throws IOException { + return toStringBuilder(supplier).toString(); + } + + /** + * 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; + } + + /** + * Returns the characters from a {@link Readable} & {@link Closeable} object + * supplied by a factory as a new {@link StringBuilder} instance. + * + * @param supplier the factory to read from + * @throws IOException if an I/O error occurs + */ + private static <R extends Readable & Closeable> StringBuilder toStringBuilder( + InputSupplier<R> supplier) throws IOException { + boolean threw = true; + R r = supplier.getInput(); + try { + StringBuilder result = toStringBuilder(r); + threw = false; + return result; + } finally { + Closeables.close(r, threw); + } + } + + /** + * 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 + */ + public static <R extends Readable & Closeable> String readFirstLine( + InputSupplier<R> supplier) throws IOException { + boolean threw = true; + R r = supplier.getInput(); + try { + String line = new LineReader(r).readLine(); + threw = false; + return line; + } finally { + Closeables.close(r, threw); + } + } + + /** + * 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 + */ + public static <R extends Readable & Closeable> List<String> readLines( + InputSupplier<R> supplier) throws IOException { + boolean threw = true; + R r = supplier.getInput(); + try { + List<String> result = readLines(r); + threw = false; + return result; + } finally { + Closeables.close(r, threw); + } + } + + /** + * 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} 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 + */ + public static <R extends Readable & Closeable, T> T readLines( + InputSupplier<R> supplier, LineProcessor<T> callback) throws IOException { + boolean threw = true; + R r = supplier.getInput(); + try { + LineReader lineReader = new LineReader(r); + String line; + while ((line = lineReader.readLine()) != null) { + if (!callback.processLine(line)) { + break; + } + } + threw = false; + } finally { + Closeables.close(r, threw); + } + return callback.getResult(); + } + + /** + * 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 + */ + public static InputSupplier<Reader> join( + final Iterable<? extends InputSupplier<? extends Reader>> suppliers) { + return new InputSupplier<Reader>() { + @Override public Reader getInput() throws IOException { + return new MultiReader(suppliers.iterator()); + } + }; + } + + /** Varargs form of {@link #join(Iterable)}. */ + 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 bytes + * @throws IOException if an I/O error occurs + */ + public static void skipFully(Reader reader, long n) throws IOException { + 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 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); + } +} diff --git a/guava/src/com/google/common/io/Closeables.java b/guava/src/com/google/common/io/Closeables.java new file mode 100644 index 0000000..e619887 --- /dev/null +++ b/guava/src/com/google/common/io/Closeables.java @@ -0,0 +1,104 @@ +/* + * 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 com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; + +import java.io.Closeable; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.annotation.Nullable; + +/** + * 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: + * + * <p><pre>public void useStreamNicely() throws IOException { + * SomeStream stream = new SomeStream("foo"); + * boolean threw = true; + * try { + * // Some code which does something with the Stream. May throw a + * // Throwable. + * threw = false; // No throwable thrown. + * } finally { + * // Close the stream. + * // If an exception occurs, only rethrow it 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; + } + } + } + + /** + * Equivalent to calling {@code close(closeable, true)}, but with no + * IOException in the signature. + * @param closeable the {@code Closeable} object to be closed, or null, in + * which case this method does nothing + */ + public static void closeQuietly(@Nullable Closeable closeable) { + try { + close(closeable, true); + } catch (IOException e) { + logger.log(Level.SEVERE, "IOException should not have been thrown.", e); + } + } +} diff --git a/guava/src/com/google/common/io/CountingInputStream.java b/guava/src/com/google/common/io/CountingInputStream.java new file mode 100644 index 0000000..d11c8ec --- /dev/null +++ b/guava/src/com/google/common/io/CountingInputStream.java @@ -0,0 +1,90 @@ +/* + * 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 com.google.common.annotations.Beta; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * 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(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; + } +} diff --git a/guava/src/com/google/common/io/CountingOutputStream.java b/guava/src/com/google/common/io/CountingOutputStream.java new file mode 100644 index 0000000..5f57714 --- /dev/null +++ b/guava/src/com/google/common/io/CountingOutputStream.java @@ -0,0 +1,59 @@ +/* + * 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 com.google.common.annotations.Beta; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * 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(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++; + } +} diff --git a/guava/src/com/google/common/io/FileBackedOutputStream.java b/guava/src/com/google/common/io/FileBackedOutputStream.java new file mode 100644 index 0000000..ce593b2 --- /dev/null +++ b/guava/src/com/google/common/io/FileBackedOutputStream.java @@ -0,0 +1,210 @@ +/* + * 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 com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * An {@link OutputStream} that starts buffering to a byte array, but + * switches to file buffering once the data reaches a configurable size. + * + * <p>This class is thread-safe. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@Beta +public final class FileBackedOutputStream extends OutputStream { + + private final int fileThreshold; + private final boolean resetOnFinalize; + private final InputSupplier<InputStream> supplier; + + private OutputStream out; + private MemoryOutput memory; + private File file; + + /** ByteArrayOutputStream that exposes its internals. */ + private static class MemoryOutput extends ByteArrayOutputStream { + byte[] getBuffer() { + return buf; + } + + int getCount() { + return count; + } + } + + /** Returns the file holding the data (possibly null). */ + @VisibleForTesting synchronized File getFile() { + return file; + } + + /** + * Creates a new instance that uses the given file threshold, and does + * not reset the data when the {@link InputSupplier} returned by + * {@link #getSupplier} is finalized. + * + * @param fileThreshold the number of bytes before the stream should + * switch to buffering to a file + */ + public FileBackedOutputStream(int fileThreshold) { + this(fileThreshold, false); + } + + /** + * Creates a new instance that uses the given file threshold, and + * optionally resets the data when the {@link InputSupplier} returned + * by {@link #getSupplier} is finalized. + * + * @param fileThreshold the number of bytes before the stream should + * switch to buffering to a file + * @param resetOnFinalize if true, the {@link #reset} method will + * be called when the {@link InputSupplier} returned by {@link + * #getSupplier} is finalized + */ + public FileBackedOutputStream(int fileThreshold, boolean resetOnFinalize) { + this.fileThreshold = fileThreshold; + this.resetOnFinalize = resetOnFinalize; + memory = new MemoryOutput(); + out = memory; + + if (resetOnFinalize) { + supplier = new InputSupplier<InputStream>() { + @Override + public InputStream getInput() throws IOException { + return openStream(); + } + + @Override protected void finalize() { + try { + reset(); + } catch (Throwable t) { + t.printStackTrace(System.err); + } + } + }; + } else { + supplier = new InputSupplier<InputStream>() { + @Override + public InputStream getInput() throws IOException { + return openStream(); + } + }; + } + } + + /** + * Returns a supplier that may be used to retrieve the data buffered + * by this stream. + */ + public InputSupplier<InputStream> getSupplier() { + return supplier; + } + + private synchronized InputStream openStream() throws IOException { + if (file != null) { + return new FileInputStream(file); + } else { + return new ByteArrayInputStream( + memory.getBuffer(), 0, memory.getCount()); + } + } + + /** + * Calls {@link #close} if not already closed, and then resets this + * object back to its initial state, for reuse. If data was buffered + * to a file, it will be deleted. + * + * @throws IOException if an I/O error occurred while deleting the file buffer + */ + public synchronized void reset() throws IOException { + try { + close(); + } finally { + if (memory == null) { + memory = new MemoryOutput(); + } else { + memory.reset(); + } + out = memory; + if (file != null) { + File deleteMe = file; + file = null; + if (!deleteMe.delete()) { + throw new IOException("Could not delete: " + deleteMe); + } + } + } + } + + @Override public synchronized void write(int b) throws IOException { + update(1); + out.write(b); + } + + @Override public synchronized void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + @Override public synchronized void write(byte[] b, int off, int len) + throws IOException { + update(len); + out.write(b, off, len); + } + + @Override public synchronized void close() throws IOException { + out.close(); + } + + @Override public synchronized void flush() throws IOException { + out.flush(); + } + + /** + * Checks if writing {@code len} bytes would go over threshold, and + * switches to file buffering if so. + */ + private void update(int len) throws IOException { + if (file == null && (memory.getCount() + len > fileThreshold)) { + File temp = File.createTempFile("FileBackedOutputStream", null); + if (resetOnFinalize) { + // Finalizers are not guaranteed to be called on system shutdown; + // this is insurance. + temp.deleteOnExit(); + } + FileOutputStream transfer = new FileOutputStream(temp); + transfer.write(memory.getBuffer(), 0, memory.getCount()); + transfer.flush(); + + // We've successfully transferred the data; switch to writing to file + out = transfer; + file = temp; + memory = null; + } + } +} diff --git a/guava/src/com/google/common/io/Files.java b/guava/src/com/google/common/io/Files.java new file mode 100644 index 0000000..71b5dee --- /dev/null +++ b/guava/src/com/google/common/io/Files.java @@ -0,0 +1,780 @@ +/* + * 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 com.google.common.annotations.Beta; +import com.google.common.base.Charsets; +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.common.hash.HashCode; +import com.google.common.hash.HashFunction; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.RandomAccessFile; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.Checksum; + +/** + * Provides utility methods for working with files. + * + * <p>All method parameters must be non-null unless documented otherwise. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@Beta +public final class Files { + + /** Maximum loop count when creating temp directories. */ + private static final int TEMP_DIR_ATTEMPTS = 10000; + + private Files() {} + + /** + * Returns a buffered reader that reads from a file using the given + * character set. + * + * @param file the file to read from + * @param charset the charset used to decode the input stream; see {@link + * Charsets} for helpful predefined constants + * @return the buffered reader + */ + public static BufferedReader newReader(File file, Charset charset) + throws FileNotFoundException { + return new BufferedReader( + new InputStreamReader(new FileInputStream(file), charset)); + } + + /** + * Returns a buffered writer that writes to a file using the given + * character set. + * + * @param file the file to write to + * @param charset the charset used to encode the output stream; see {@link + * Charsets} for helpful predefined constants + * @return the buffered writer + */ + public static BufferedWriter newWriter(File file, Charset charset) + throws FileNotFoundException { + return new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(file), charset)); + } + + /** + * Returns a factory that will supply instances of {@link FileInputStream} + * that read from a file. + * + * @param file the file to read from + * @return the factory + */ + public static InputSupplier<FileInputStream> newInputStreamSupplier( + final File file) { + Preconditions.checkNotNull(file); + return new InputSupplier<FileInputStream>() { + @Override + public FileInputStream getInput() throws IOException { + return new FileInputStream(file); + } + }; + } + + /** + * Returns a factory that will supply instances of {@link FileOutputStream} + * that write to a file. + * + * @param file the file to write to + * @return the factory + */ + public static OutputSupplier<FileOutputStream> newOutputStreamSupplier( + File file) { + return newOutputStreamSupplier(file, false); + } + + /** + * Returns a factory that will supply instances of {@link FileOutputStream} + * that write to or append to a file. + * + * @param file the file to write to + * @param append if true, the encoded characters will be appended to the file; + * otherwise the file is overwritten + * @return the factory + */ + public static OutputSupplier<FileOutputStream> newOutputStreamSupplier( + final File file, final boolean append) { + Preconditions.checkNotNull(file); + return new OutputSupplier<FileOutputStream>() { + @Override + public FileOutputStream getOutput() throws IOException { + return new FileOutputStream(file, append); + } + }; + } + + /** + * Returns a factory that will supply instances of + * {@link InputStreamReader} that read a file using the given character set. + * + * @param file the file to read from + * @param charset the charset used to decode the input stream; see {@link + * Charsets} for helpful predefined constants + * @return the factory + */ + public static InputSupplier<InputStreamReader> newReaderSupplier(File file, + Charset charset) { + return CharStreams.newReaderSupplier(newInputStreamSupplier(file), charset); + } + + /** + * Returns a factory that will supply instances of {@link OutputStreamWriter} + * that write to a file using the given character set. + * + * @param file the file to write to + * @param charset the charset used to encode the output stream; see {@link + * Charsets} for helpful predefined constants + * @return the factory + */ + public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file, + Charset charset) { + return newWriterSupplier(file, charset, false); + } + + /** + * Returns a factory that will supply instances of {@link OutputStreamWriter} + * that write to or append to a file using the given character set. + * + * @param file the file to write to + * @param charset the charset used to encode the output stream; see {@link + * Charsets} for helpful predefined constants + * @param append if true, the encoded characters will be appended to the file; + * otherwise the file is overwritten + * @return the factory + */ + public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file, + Charset charset, boolean append) { + return CharStreams.newWriterSupplier(newOutputStreamSupplier(file, append), + charset); + } + + /** + * Reads all bytes from a file into a byte array. + * + * @param file the file to read from + * @return a byte array containing all the bytes from file + * @throws IllegalArgumentException if the file is bigger than the largest + * possible byte array (2^31 - 1) + * @throws IOException if an I/O error occurs + */ + public static byte[] toByteArray(File file) throws IOException { + Preconditions.checkArgument(file.length() <= Integer.MAX_VALUE); + if (file.length() == 0) { + // Some special files are length 0 but have content nonetheless. + return ByteStreams.toByteArray(newInputStreamSupplier(file)); + } else { + // Avoid an extra allocation and copy. + byte[] b = new byte[(int) file.length()]; + boolean threw = true; + InputStream in = new FileInputStream(file); + try { + ByteStreams.readFully(in, b); + threw = false; + } finally { + Closeables.close(in, threw); + } + return b; + } + } + + /** + * Reads all characters from a file into a {@link String}, using the given + * character set. + * + * @param file the file 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 file + * @throws IOException if an I/O error occurs + */ + public static String toString(File file, Charset charset) throws IOException { + return new String(toByteArray(file), charset.name()); + } + + /** + * Copies to a file all bytes from an {@link InputStream} supplied by a + * factory. + * + * @param from the input factory + * @param to the destination file + * @throws IOException if an I/O error occurs + */ + public static void copy(InputSupplier<? extends InputStream> from, File to) + throws IOException { + ByteStreams.copy(from, newOutputStreamSupplier(to)); + } + + /** + * Overwrites a file with the contents of a byte array. + * + * @param from the bytes to write + * @param to the destination file + * @throws IOException if an I/O error occurs + */ + public static void write(byte[] from, File to) throws IOException { + ByteStreams.write(from, newOutputStreamSupplier(to)); + } + + /** + * Copies all bytes from a file to an {@link OutputStream} supplied by + * a factory. + * + * @param from the source file + * @param to the output factory + * @throws IOException if an I/O error occurs + */ + public static void copy(File from, OutputSupplier<? extends OutputStream> to) + throws IOException { + ByteStreams.copy(newInputStreamSupplier(from), to); + } + + /** + * Copies all bytes from a file to an output stream. + * + * @param from the source file + * @param to the output stream + * @throws IOException if an I/O error occurs + */ + public static void copy(File from, OutputStream to) throws IOException { + ByteStreams.copy(newInputStreamSupplier(from), to); + } + + /** + * Copies all the bytes from one file to another. + *. + * @param from the source file + * @param to the destination file + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if {@code from.equals(to)} + */ + public static void copy(File from, File to) throws IOException { + Preconditions.checkArgument(!from.equals(to), + "Source %s and destination %s must be different", from, to); + copy(newInputStreamSupplier(from), to); + } + + /** + * Copies to a file all characters from a {@link Readable} and + * {@link Closeable} object supplied by a factory, using the given + * character set. + * + * @param from the readable supplier + * @param to the destination file + * @param charset the charset used to encode the output stream; see {@link + * Charsets} for helpful predefined constants + * @throws IOException if an I/O error occurs + */ + public static <R extends Readable & Closeable> void copy( + InputSupplier<R> from, File to, Charset charset) throws IOException { + CharStreams.copy(from, newWriterSupplier(to, charset)); + } + + /** + * Writes a character sequence (such as a string) to a file using the given + * character set. + * + * @param from the character sequence to write + * @param to the destination file + * @param charset the charset used to encode the output stream; see {@link + * Charsets} for helpful predefined constants + * @throws IOException if an I/O error occurs + */ + public static void write(CharSequence from, File to, Charset charset) + throws IOException { + write(from, to, charset, false); + } + + /** + * Appends a character sequence (such as a string) to a file using the given + * character set. + * + * @param from the character sequence to append + * @param to the destination file + * @param charset the charset used to encode the output stream; see {@link + * Charsets} for helpful predefined constants + * @throws IOException if an I/O error occurs + */ + public static void append(CharSequence from, File to, Charset charset) + throws IOException { + write(from, to, charset, true); + } + + /** + * Private helper method. Writes a character sequence to a file, + * optionally appending. + * + * @param from the character sequence to append + * @param to the destination file + * @param charset the charset used to encode the output stream; see {@link + * Charsets} for helpful predefined constants + * @param append true to append, false to overwrite + * @throws IOException if an I/O error occurs + */ + private static void write(CharSequence from, File to, Charset charset, + boolean append) throws IOException { + CharStreams.write(from, newWriterSupplier(to, charset, append)); + } + + /** + * Copies all characters from a file to a {@link Appendable} & + * {@link Closeable} object supplied by a factory, using the given + * character set. + * + * @param from the source file + * @param charset the charset used to decode the input stream; see {@link + * Charsets} for helpful predefined constants + * @param to the appendable supplier + * @throws IOException if an I/O error occurs + */ + public static <W extends Appendable & Closeable> void copy(File from, + Charset charset, OutputSupplier<W> to) throws IOException { + CharStreams.copy(newReaderSupplier(from, charset), to); + } + + /** + * Copies all characters from a file to an appendable object, + * using the given character set. + * + * @param from the source file + * @param charset the charset used to decode the input stream; see {@link + * Charsets} for helpful predefined constants + * @param to the appendable object + * @throws IOException if an I/O error occurs + */ + public static void copy(File from, Charset charset, Appendable to) + throws IOException { + CharStreams.copy(newReaderSupplier(from, charset), to); + } + + /** + * Returns true if the files contains the same bytes. + * + * @throws IOException if an I/O error occurs + */ + public static boolean equal(File file1, File file2) throws IOException { + if (file1 == file2 || file1.equals(file2)) { + return true; + } + + /* + * Some operating systems may return zero as the length for files + * denoting system-dependent entities such as devices or pipes, in + * which case we must fall back on comparing the bytes directly. + */ + long len1 = file1.length(); + long len2 = file2.length(); + if (len1 != 0 && len2 != 0 && len1 != len2) { + return false; + } + return ByteStreams.equal(newInputStreamSupplier(file1), + newInputStreamSupplier(file2)); + } + + /** + * Atomically creates a new directory somewhere beneath the system's + * temporary directory (as defined by the {@code java.io.tmpdir} system + * property), and returns its name. + * + * <p>Use this method instead of {@link File#createTempFile(String, String)} + * when you wish to create a directory, not a regular file. A common pitfall + * is to call {@code createTempFile}, delete the file and create a + * directory in its place, but this leads a race condition which can be + * exploited to create security vulnerabilities, especially when executable + * files are to be written into the directory. + * + * <p>This method assumes that the temporary volume is writable, has free + * inodes and free blocks, and that it will not be called thousands of times + * per second. + * + * @return the newly-created directory + * @throws IllegalStateException if the directory could not be created + */ + public static File createTempDir() { + File baseDir = new File(System.getProperty("java.io.tmpdir")); + String baseName = System.currentTimeMillis() + "-"; + + for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) { + File tempDir = new File(baseDir, baseName + counter); + if (tempDir.mkdir()) { + return tempDir; + } + } + throw new IllegalStateException("Failed to create directory within " + + TEMP_DIR_ATTEMPTS + " attempts (tried " + + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')'); + } + + /** + * Creates an empty file or updates the last updated timestamp on the + * same as the unix command of the same name. + * + * @param file the file to create or update + * @throws IOException if an I/O error occurs + */ + public static void touch(File file) throws IOException { + if (!file.createNewFile() + && !file.setLastModified(System.currentTimeMillis())) { + throw new IOException("Unable to update modification time of " + file); + } + } + + /** + * Creates any necessary but nonexistent parent directories of the specified + * file. Note that if this operation fails it may have succeeded in creating + * some (but not all) of the necessary parent directories. + * + * @throws IOException if an I/O error occurs, or if any necessary but + * nonexistent parent directories of the specified file could not be + * created. + * @since 4.0 + */ + public static void createParentDirs(File file) throws IOException { + File parent = file.getCanonicalFile().getParentFile(); + if (parent == null) { + /* + * The given directory is a filesystem root. All zero of its ancestors + * exist. This doesn't mean that the root itself exists -- consider x:\ on + * a Windows machine without such a drive -- or even that the caller can + * create it, but this method makes no such guarantees even for non-root + * files. + */ + return; + } + parent.mkdirs(); + if (!parent.isDirectory()) { + throw new IOException("Unable to create parent directories of " + file); + } + } + + /** + * Moves the file from one path to another. This method can rename a file or + * move it to a different directory, like the Unix {@code mv} command. + * + * @param from the source file + * @param to the destination file + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if {@code from.equals(to)} + */ + public static void move(File from, File to) throws IOException { + Preconditions.checkNotNull(to); + Preconditions.checkArgument(!from.equals(to), + "Source %s and destination %s must be different", from, to); + + if (!from.renameTo(to)) { + copy(from, to); + if (!from.delete()) { + if (!to.delete()) { + throw new IOException("Unable to delete " + to); + } + throw new IOException("Unable to delete " + from); + } + } + } + + /** + * Reads the first line from a file. The line does not include + * line-termination characters, but does include other leading and + * trailing whitespace. + * + * @param file the file to read from + * @param charset the charset used to decode the input stream; see {@link + * Charsets} for helpful predefined constants + * @return the first line, or null if the file is empty + * @throws IOException if an I/O error occurs + */ + public static String readFirstLine(File file, Charset charset) + throws IOException { + return CharStreams.readFirstLine(Files.newReaderSupplier(file, charset)); + } + + /** + * Reads all of the lines from a file. The lines do not include + * line-termination characters, but do include other leading and + * trailing whitespace. + * + * @param file the file 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(File file, Charset charset) + throws IOException { + return CharStreams.readLines(Files.newReaderSupplier(file, charset)); + } + + /** + * Streams lines from a {@link File}, stopping when our callback returns + * false, or we have read all of the lines. + * + * @param file the file to read from + * @param charset the charset used to decode the input stream; see {@link + * Charsets} for helpful predefined constants + * @param callback the {@link 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(File file, Charset charset, + LineProcessor<T> callback) throws IOException { + return CharStreams.readLines(Files.newReaderSupplier(file, charset), + callback); + } + + /** + * Process the bytes of a file. + * + * <p>(If this seems too complicated, maybe you're looking for + * {@link #toByteArray}.) + * + * @param file the file to read + * @param processor the object to which the bytes of the file are passed. + * @return the result of the byte processor + * @throws IOException if an I/O error occurs + */ + public static <T> T readBytes(File file, ByteProcessor<T> processor) + throws IOException { + return ByteStreams.readBytes(newInputStreamSupplier(file), processor); + } + + /** + * Computes and returns the checksum value for a file. + * The checksum object is reset when this method returns successfully. + * + * @param file the file to read + * @param checksum the checksum object + * @return the result of {@link Checksum#getValue} after updating the + * checksum object with all of the bytes in the file + * @throws IOException if an I/O error occurs + */ + public static long getChecksum(File file, Checksum checksum) + throws IOException { + return ByteStreams.getChecksum(newInputStreamSupplier(file), checksum); + } + + /** + * Computes the hash code of the {@code file} using {@code hashFunction}. + * + * @param file the file to read + * @param hashFunction the hash function to use to hash the data + * @return the {@link HashCode} of all of the bytes in the file + * @throws IOException if an I/O error occurs + * @since 12.0 + */ + public static HashCode hash(File file, HashFunction hashFunction) + throws IOException { + return ByteStreams.hash(newInputStreamSupplier(file), hashFunction); + } + + /** + * Fully maps a file read-only in to memory as per + * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}. + * + * <p>Files are mapped from offset 0 to its length. + * + * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes. + * + * @param file the file to map + * @return a read-only buffer reflecting {@code file} + * @throws FileNotFoundException if the {@code file} does not exist + * @throws IOException if an I/O error occurs + * + * @see FileChannel#map(MapMode, long, long) + * @since 2.0 + */ + public static MappedByteBuffer map(File file) throws IOException { + return map(file, MapMode.READ_ONLY); + } + + /** + * Fully maps a file in to memory as per + * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)} + * using the requested {@link MapMode}. + * + * <p>Files are mapped from offset 0 to its length. + * + * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes. + * + * @param file the file to map + * @param mode the mode to use when mapping {@code file} + * @return a buffer reflecting {@code file} + * @throws FileNotFoundException if the {@code file} does not exist + * @throws IOException if an I/O error occurs + * + * @see FileChannel#map(MapMode, long, long) + * @since 2.0 + */ + public static MappedByteBuffer map(File file, MapMode mode) + throws IOException { + if (!file.exists()) { + throw new FileNotFoundException(file.toString()); + } + return map(file, mode, file.length()); + } + + /** + * Maps a file in to memory as per + * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)} + * using the requested {@link MapMode}. + * + * <p>Files are mapped from offset 0 to {@code size}. + * + * <p>If the mode is {@link MapMode#READ_WRITE} and the file does not exist, + * it will be created with the requested {@code size}. Thus this method is + * useful for creating memory mapped files which do not yet exist. + * + * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes. + * + * @param file the file to map + * @param mode the mode to use when mapping {@code file} + * @return a buffer reflecting {@code file} + * @throws IOException if an I/O error occurs + * + * @see FileChannel#map(MapMode, long, long) + * @since 2.0 + */ + public static MappedByteBuffer map(File file, MapMode mode, long size) + throws FileNotFoundException, IOException { + RandomAccessFile raf = + new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw"); + + boolean threw = true; + try { + MappedByteBuffer mbb = map(raf, mode, size); + threw = false; + return mbb; + } finally { + Closeables.close(raf, threw); + } + } + + private static MappedByteBuffer map(RandomAccessFile raf, MapMode mode, + long size) throws IOException { + FileChannel channel = raf.getChannel(); + + boolean threw = true; + try { + MappedByteBuffer mbb = channel.map(mode, 0, size); + threw = false; + return mbb; + } finally { + Closeables.close(channel, threw); + } + } + + /** + * Returns the lexically cleaned form of the path name, <i>usually</i> (but + * not always) equivalent to the original. The following heuristics are used: + * + * <ul> + * <li>empty string becomes . + * <li>. stays as . + * <li>fold out ./ + * <li>fold out ../ when possible + * <li>collapse multiple slashes + * <li>delete trailing slashes (unless the path is just "/") + * </ul> + * + * These heuristics do not always match the behavior of the filesystem. In + * particular, consider the path {@code a/../b}, which {@code simplifyPath} + * will change to {@code b}. If {@code a} is a symlink to {@code x}, {@code + * a/../b} may refer to a sibling of {@code x}, rather than the sibling of + * {@code a} referred to by {@code b}. + * + * @since 11.0 + */ + public static String simplifyPath(String pathname) { + if (pathname.length() == 0) { + return "."; + } + + // split the path apart + Iterable<String> components = + Splitter.on('/').omitEmptyStrings().split(pathname); + List<String> path = new ArrayList<String>(); + + // resolve ., .., and // + for (String component : components) { + if (component.equals(".")) { + continue; + } else if (component.equals("..")) { + if (path.size() > 0 && !path.get(path.size() - 1).equals("..")) { + path.remove(path.size() - 1); + } else { + path.add(".."); + } + } else { + path.add(component); + } + } + + // put it back together + String result = Joiner.on('/').join(path); + if (pathname.charAt(0) == '/') { + result = "/" + result; + } + + while (result.startsWith("/../")) { + result = result.substring(3); + } + if (result.equals("/..")) { + result = "/"; + } else if ("".equals(result)) { + result = "."; + } + + return result; + } + + /** + * Returns the <a href="http://en.wikipedia.org/wiki/Filename_extension">file + * extension</a> for the given file name, or the empty string if the file has + * no extension. The result does not include the '{@code .}'. + * + * @since 11.0 + */ + public static String getFileExtension(String fileName) { + checkNotNull(fileName); + int dotIndex = fileName.lastIndexOf('.'); + return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1); + } +} diff --git a/guava/src/com/google/common/io/Flushables.java b/guava/src/com/google/common/io/Flushables.java new file mode 100644 index 0000000..68ff860 --- /dev/null +++ b/guava/src/com/google/common/io/Flushables.java @@ -0,0 +1,80 @@ +/* + * 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 com.google.common.annotations.Beta; + +import java.io.Flushable; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * 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); + } + } +} diff --git a/guava/src/com/google/common/io/InputSupplier.java b/guava/src/com/google/common/io/InputSupplier.java new file mode 100644 index 0000000..2b6a4ad --- /dev/null +++ b/guava/src/com/google/common/io/InputSupplier.java @@ -0,0 +1,40 @@ +/* + * 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 + */ +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; +} diff --git a/guava/src/com/google/common/io/LimitInputStream.java b/guava/src/com/google/common/io/LimitInputStream.java new file mode 100644 index 0000000..e9d963d --- /dev/null +++ b/guava/src/com/google/common/io/LimitInputStream.java @@ -0,0 +1,104 @@ +/* + * 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 com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * An InputStream that limits the number of bytes which can be read. + * + * @author Charles Fry + * @since 1.0 + */ +@Beta +public final class LimitInputStream extends FilterInputStream { + + private long left; + private long mark = -1; + + /** + * Wraps another input stream, limiting the number of bytes which can be read. + * + * @param in the input stream to be wrapped + * @param limit the maximum number of bytes to be read + */ + public LimitInputStream(InputStream in, long limit) { + super(in); + Preconditions.checkNotNull(in); + Preconditions.checkArgument(limit >= 0, "limit must be non-negative"); + left = limit; + } + + @Override public int available() throws IOException { + return (int) Math.min(in.available(), left); + } + + @Override public synchronized void mark(int readlimit) { + in.mark(readlimit); + mark = left; + // it's okay to mark even if mark isn't supported, as reset won't work + } + + @Override public int read() throws IOException { + if (left == 0) { + return -1; + } + + int result = in.read(); + if (result != -1) { + --left; + } + return result; + } + + @Override public int read(byte[] b, int off, int len) throws IOException { + if (left == 0) { + return -1; + } + + len = (int) Math.min(len, left); + int result = in.read(b, off, len); + if (result != -1) { + left -= result; + } + return result; + } + + @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(); + left = mark; + } + + @Override public long skip(long n) throws IOException { + n = Math.min(n, left); + long skipped = in.skip(n); + left -= skipped; + return skipped; + } +} diff --git a/guava/src/com/google/common/io/LineBuffer.java b/guava/src/com/google/common/io/LineBuffer.java new file mode 100644 index 0000000..1f1c8dc --- /dev/null +++ b/guava/src/com/google/common/io/LineBuffer.java @@ -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; + } + } + 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; +} diff --git a/guava/src/com/google/common/io/LineProcessor.java b/guava/src/com/google/common/io/LineProcessor.java new file mode 100644 index 0000000..9ee6a0f --- /dev/null +++ b/guava/src/com/google/common/io/LineProcessor.java @@ -0,0 +1,45 @@ +/* + * 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 com.google.common.annotations.Beta; + +import java.io.IOException; + +/** + * 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(); +} diff --git a/guava/src/com/google/common/io/LineReader.java b/guava/src/com/google/common/io/LineReader.java new file mode 100644 index 0000000..69ae0cf --- /dev/null +++ b/guava/src/com/google/common/io/LineReader.java @@ -0,0 +1,87 @@ +/* + * 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 com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; + +import java.io.IOException; +import java.io.Reader; +import java.nio.CharBuffer; +import java.util.LinkedList; +import java.util.Queue; + +/** + * 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) { + Preconditions.checkNotNull(readable); + this.readable = 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(); + } +} diff --git a/guava/src/com/google/common/io/LittleEndianDataInputStream.java b/guava/src/com/google/common/io/LittleEndianDataInputStream.java new file mode 100644 index 0000000..e60d22f --- /dev/null +++ b/guava/src/com/google/common/io/LittleEndianDataInputStream.java @@ -0,0 +1,232 @@ +/* + * 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 com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * 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; + } + +} diff --git a/guava/src/com/google/common/io/LittleEndianDataOutputStream.java b/guava/src/com/google/common/io/LittleEndianDataOutputStream.java new file mode 100644 index 0000000..1725ae0 --- /dev/null +++ b/guava/src/com/google/common/io/LittleEndianDataOutputStream.java @@ -0,0 +1,164 @@ +/* + * 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 com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.primitives.Longs; + +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * 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); + } +} diff --git a/guava/src/com/google/common/io/MultiInputStream.java b/guava/src/com/google/common/io/MultiInputStream.java new file mode 100644 index 0000000..f8f1a0b --- /dev/null +++ b/guava/src/com/google/common/io/MultiInputStream.java @@ -0,0 +1,115 @@ +/* + * 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; +import java.io.InputStream; +import java.util.Iterator; + +/** + * 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 InputSupplier<? extends InputStream>> 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 InputSupplier<? extends InputStream>> it) + throws IOException { + this.it = 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().getInput(); + } + } + + @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(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); + } +} diff --git a/guava/src/com/google/common/io/MultiReader.java b/guava/src/com/google/common/io/MultiReader.java new file mode 100644 index 0000000..6757a26 --- /dev/null +++ b/guava/src/com/google/common/io/MultiReader.java @@ -0,0 +1,90 @@ +/* + * 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 com.google.common.base.Preconditions; + +import java.io.IOException; +import java.io.Reader; +import java.util.Iterator; + +/** + * A {@link Reader} that concatenates multiple readers. + * + * @author Bin Zhu + * @since 1.0 + */ +class MultiReader extends Reader { + private final Iterator<? extends InputSupplier<? extends Reader>> it; + private Reader current; + + MultiReader(Iterator<? extends InputSupplier<? extends Reader>> 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().getInput(); + } + } + + @Override public int read(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; + } + } + } +} diff --git a/guava/src/com/google/common/io/NullOutputStream.java b/guava/src/com/google/common/io/NullOutputStream.java new file mode 100644 index 0000000..1c1e98e --- /dev/null +++ b/guava/src/com/google/common/io/NullOutputStream.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2004 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 com.google.common.annotations.Beta; + +import java.io.OutputStream; + +/** + * Implementation of {@link OutputStream} that simply discards written bytes. + * + * @author Spencer Kimball + * @since 1.0 + */ +@Beta +public final class NullOutputStream extends OutputStream { + /** Discards the specified byte. */ + @Override public void write(int b) { + } + + /** Discards the specified byte array. */ + @Override public void write(byte[] b, int off, int len) { + } +} diff --git a/guava/src/com/google/common/io/OutputSupplier.java b/guava/src/com/google/common/io/OutputSupplier.java new file mode 100644 index 0000000..185082c --- /dev/null +++ b/guava/src/com/google/common/io/OutputSupplier.java @@ -0,0 +1,40 @@ +/* + * 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 + */ +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; +} diff --git a/guava/src/com/google/common/io/PatternFilenameFilter.java b/guava/src/com/google/common/io/PatternFilenameFilter.java new file mode 100644 index 0000000..2859277 --- /dev/null +++ b/guava/src/com/google/common/io/PatternFilenameFilter.java @@ -0,0 +1,62 @@ +/* + * 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 com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import javax.annotation.Nullable; + +/** + * File name filter that only accepts files matching a regular expression. This + * class is thread-safe and immutable. + * + * @author Apple Chow + * @since 1.0 + */ +@Beta +public final class PatternFilenameFilter implements FilenameFilter { + + private final Pattern pattern; + + /** + * Constructs a pattern file name filter object. + * @param patternStr the pattern string on which to filter file names + * + * @throws PatternSyntaxException if pattern compilation fails (runtime) + */ + public PatternFilenameFilter(String patternStr) { + this(Pattern.compile(patternStr)); + } + + /** + * Constructs a pattern file name filter object. + * @param pattern the pattern on which to filter file names + */ + public PatternFilenameFilter(Pattern pattern) { + this.pattern = Preconditions.checkNotNull(pattern); + } + + @Override public boolean accept(@Nullable File dir, String fileName) { + return pattern.matcher(fileName).matches(); + } +} diff --git a/guava/src/com/google/common/io/Resources.java b/guava/src/com/google/common/io/Resources.java new file mode 100644 index 0000000..1bb8067 --- /dev/null +++ b/guava/src/com/google/common/io/Resources.java @@ -0,0 +1,173 @@ +/* + * 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 com.google.common.annotations.Beta; +import com.google.common.base.Charsets; + +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; + +/** + * 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 + * @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 + */ + public static InputSupplier<InputStream> newInputStreamSupplier( + final URL url) { + checkNotNull(url); + return new InputSupplier<InputStream>() { + @Override + public InputStream getInput() throws IOException { + return url.openStream(); + } + }; + } + + /** + * 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 + */ + public static InputSupplier<InputStreamReader> newReaderSupplier( + URL url, Charset charset) { + return CharStreams.newReaderSupplier(newInputStreamSupplier(url), 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 ByteStreams.toByteArray(newInputStreamSupplier(url)); + } + + /** + * 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 CharStreams.toString(newReaderSupplier(url, charset)); + } + + /** + * 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. + * + * @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 { + return CharStreams.readLines(newReaderSupplier(url, charset)); + } + + /** + * 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 { + ByteStreams.copy(newInputStreamSupplier(from), to); + } + + /** + * Returns a {@code URL} pointing to {@code resourceName} if the resource is + * found in the class path. {@code Resources.class.getClassLoader()} is used + * to locate the resource. + * + * @throws IllegalArgumentException if resource is not found + */ + public static URL getResource(String resourceName) { + URL url = Resources.class.getClassLoader().getResource(resourceName); + checkArgument(url != null, "resource %s not found.", resourceName); + return url; + } + + /** + * Returns a {@code URL} pointing to {@code resourceName} that is relative to + * {@code contextClass}, if the resource is found in the class path. + * + * @throws IllegalArgumentException if 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; + } +} diff --git a/guava/src/com/google/common/io/package-info.java b/guava/src/com/google/common/io/package-info.java new file mode 100644 index 0000000..f5f83ad --- /dev/null +++ b/guava/src/com/google/common/io/package-info.java @@ -0,0 +1,41 @@ +/* + * 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>Many of the methods are based on the + * {@link com.google.common.io.InputSupplier} and + * {@link com.google.common.io.OutputSupplier} interfaces. They are used as + * factories for I/O objects that might throw {@link java.io.IOException} when + * being created. The advantage of using a factory is that the helper methods in + * this package can take care of closing the resource properly, even if an + * exception is thrown. The {@link com.google.common.io.ByteStreams}, + * {@link com.google.common.io.CharStreams}, and + * {@link com.google.common.io.Files} classes all have static helper methods to + * create new factories and to work with them. + * + * <p>This package is a part of the open-source + * <a href="http://guava-libraries.googlecode.com">Guava libraries</a>. + * + * @author Chris Nokleberg + */ +@ParametersAreNonnullByDefault +package com.google.common.io; + +import javax.annotation.ParametersAreNonnullByDefault; + |