Fixed JavaDoc errors to enable Java 8 build.

This commit is contained in:
Harald Kuhr
2019-08-10 00:41:36 +02:00
parent 7d2c692663
commit 9e23413456
168 changed files with 34586 additions and 34396 deletions

View File

@@ -218,9 +218,10 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
/**
* Writes a series of bytes at the current read/write position. The read/write position will be increased by
* {@code pLength}.
* <p/>
* <p>
* This implementation invokes {@link #write(int)} {@code pLength} times.
* Subclasses may override this method for performance.
* </p>
*
* @param pBuffer the bytes to write.
* @param pOffset the starting offset into the buffer.
@@ -246,9 +247,10 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
/**
* Writes a series of bytes at the current read/write position. The read/write position will be increased by
* {@code pLength}.
* <p/>
* <p>
* This implementation invokes {@link #read()} {@code pLength} times.
* Subclasses may override this method for performance.
* </p>
*
* @param pBuffer the bytes to write
* @param pOffset the starting offset into the buffer.
@@ -283,12 +285,14 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
/**
* Optionally flushes any data prior to the given position.
* <p/>
* <p>
* Attempting to perform a seek operation, and/or a read or write operation to a position equal to or before
* the flushed position may result in exceptions or undefined behaviour.
* <p/>
* </p>
* <p>
* Subclasses should override this method for performance reasons, to avoid holding on to unnecessary resources.
* This implementation does nothing.
* </p>
*
* @param pPosition the last position to flush.
*/

View File

@@ -1,222 +1,221 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* A Reader implementation that can read from multiple sources.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/CompoundReader.java#2 $
*/
public class CompoundReader extends Reader {
private Reader current;
private List<Reader> readers;
protected final Object finalLock;
protected final boolean markSupported;
private int currentReader;
private int markedReader;
private int mark;
private int mNext;
/**
* Create a new compound reader.
*
* @param pReaders {@code Iterator} containting {@code Reader}s,
* providing the character stream.
*
* @throws NullPointerException if {@code pReaders} is {@code null}, or
* any of the elements in the iterator is {@code null}.
* @throws ClassCastException if any element of the iterator is not a
* {@code java.io.Reader}
*/
public CompoundReader(final Iterator<Reader> pReaders) {
super(Validate.notNull(pReaders, "readers"));
finalLock = pReaders; // NOTE: It's ok to sync on pReaders, as the
// reference can't change, only it's elements
readers = new ArrayList<Reader>();
boolean markSupported = true;
while (pReaders.hasNext()) {
Reader reader = pReaders.next();
if (reader == null) {
throw new NullPointerException("readers cannot contain null-elements");
}
readers.add(reader);
markSupported = markSupported && reader.markSupported();
}
this.markSupported = markSupported;
current = nextReader();
}
protected final Reader nextReader() {
if (currentReader >= readers.size()) {
current = new EmptyReader();
}
else {
current = readers.get(currentReader++);
}
// NOTE: Reset mNext for every reader, and record marked reader in mark/reset methods!
mNext = 0;
return current;
}
/**
* Check to make sure that the stream has not been closed
*
* @throws IOException if the stream is closed
*/
protected final void ensureOpen() throws IOException {
if (readers == null) {
throw new IOException("Stream closed");
}
}
public void close() throws IOException {
// Close all readers
for (Reader reader : readers) {
reader.close();
}
readers = null;
}
@Override
public void mark(int pReadLimit) throws IOException {
if (pReadLimit < 0) {
throw new IllegalArgumentException("Read limit < 0");
}
// TODO: It would be nice if we could actually close some readers now
synchronized (finalLock) {
ensureOpen();
mark = mNext;
markedReader = currentReader;
current.mark(pReadLimit);
}
}
@Override
public void reset() throws IOException {
synchronized (finalLock) {
ensureOpen();
if (currentReader != markedReader) {
// Reset any reader before this
for (int i = currentReader; i >= markedReader; i--) {
readers.get(i).reset();
}
currentReader = markedReader - 1;
nextReader();
}
current.reset();
mNext = mark;
}
}
@Override
public boolean markSupported() {
return markSupported;
}
@Override
public int read() throws IOException {
synchronized (finalLock) {
int read = current.read();
if (read < 0 && currentReader < readers.size()) {
nextReader();
return read(); // In case of 0-length readers
}
mNext++;
return read;
}
}
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
synchronized (finalLock) {
int read = current.read(pBuffer, pOffset, pLength);
if (read < 0 && currentReader < readers.size()) {
nextReader();
return read(pBuffer, pOffset, pLength); // In case of 0-length readers
}
mNext += read;
return read;
}
}
@Override
public boolean ready() throws IOException {
return current.ready();
}
@Override
public long skip(long pChars) throws IOException {
synchronized (finalLock) {
long skipped = current.skip(pChars);
if (skipped == 0 && currentReader < readers.size()) {
nextReader();
return skip(pChars); // In case of 0-length readers
}
mNext += skipped;
return skipped;
}
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* A Reader implementation that can read from multiple sources.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/CompoundReader.java#2 $
*/
public class CompoundReader extends Reader {
private Reader current;
private List<Reader> readers;
protected final Object finalLock;
protected final boolean markSupported;
private int currentReader;
private int markedReader;
private int mark;
private int mNext;
/**
* Create a new compound reader.
*
* @param pReaders {@code Iterator} containting {@code Reader}s,
* providing the character stream.
*
* @throws NullPointerException if {@code pReaders} is {@code null}, or
* any of the elements in the iterator is {@code null}.
* @throws ClassCastException if any element of the iterator is not a
* {@code java.io.Reader}
*/
public CompoundReader(final Iterator<Reader> pReaders) {
super(Validate.notNull(pReaders, "readers"));
finalLock = pReaders; // NOTE: It's ok to sync on pReaders, as the
// reference can't change, only it's elements
readers = new ArrayList<Reader>();
boolean markSupported = true;
while (pReaders.hasNext()) {
Reader reader = pReaders.next();
if (reader == null) {
throw new NullPointerException("readers cannot contain null-elements");
}
readers.add(reader);
markSupported = markSupported && reader.markSupported();
}
this.markSupported = markSupported;
current = nextReader();
}
protected final Reader nextReader() {
if (currentReader >= readers.size()) {
current = new EmptyReader();
}
else {
current = readers.get(currentReader++);
}
// NOTE: Reset mNext for every reader, and record marked reader in mark/reset methods!
mNext = 0;
return current;
}
/**
* Check to make sure that the stream has not been closed
*
* @throws IOException if the stream is closed
*/
protected final void ensureOpen() throws IOException {
if (readers == null) {
throw new IOException("Stream closed");
}
}
public void close() throws IOException {
// Close all readers
for (Reader reader : readers) {
reader.close();
}
readers = null;
}
@Override
public void mark(int pReadLimit) throws IOException {
if (pReadLimit < 0) {
throw new IllegalArgumentException("Read limit < 0");
}
// TODO: It would be nice if we could actually close some readers now
synchronized (finalLock) {
ensureOpen();
mark = mNext;
markedReader = currentReader;
current.mark(pReadLimit);
}
}
@Override
public void reset() throws IOException {
synchronized (finalLock) {
ensureOpen();
if (currentReader != markedReader) {
// Reset any reader before this
for (int i = currentReader; i >= markedReader; i--) {
readers.get(i).reset();
}
currentReader = markedReader - 1;
nextReader();
}
current.reset();
mNext = mark;
}
}
@Override
public boolean markSupported() {
return markSupported;
}
@Override
public int read() throws IOException {
synchronized (finalLock) {
int read = current.read();
if (read < 0 && currentReader < readers.size()) {
nextReader();
return read(); // In case of 0-length readers
}
mNext++;
return read;
}
}
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
synchronized (finalLock) {
int read = current.read(pBuffer, pOffset, pLength);
if (read < 0 && currentReader < readers.size()) {
nextReader();
return read(pBuffer, pOffset, pLength); // In case of 0-length readers
}
mNext += read;
return read;
}
}
@Override
public boolean ready() throws IOException {
return current.ready();
}
@Override
public long skip(long pChars) throws IOException {
synchronized (finalLock) {
long skipped = current.skip(pChars);
if (skipped == 0 && currentReader < readers.size()) {
nextReader();
return skip(pChars); // In case of 0-length readers
}
mNext += skipped;
return skipped;
}
}
}

View File

@@ -1,47 +1,46 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.StringReader;
/**
* EmptyReader
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/EmptyReader.java#1 $
*/
final class EmptyReader extends StringReader {
public EmptyReader() {
super("");
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.StringReader;
/**
* EmptyReader
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/EmptyReader.java#1 $
*/
final class EmptyReader extends StringReader {
public EmptyReader() {
super("");
}
}

View File

@@ -1,136 +1,137 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version
* also has a constructor that lets you create a stream with initial content.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: FastByteArrayOutputStream.java#2 $
*/
// TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block
public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
/** Max grow size (unless if writing more than this amount of bytes) */
protected int maxGrowSize = 1024 * 1024; // 1 MB
/**
* Creates a {@code ByteArrayOutputStream} with the given initial buffer
* size.
*
* @param pSize initial buffer size
*/
public FastByteArrayOutputStream(int pSize) {
super(pSize);
}
/**
* Creates a {@code ByteArrayOutputStream} with the given initial content.
* <p/>
* Note that the buffer is not cloned, for maximum performance.
*
* @param pBuffer initial buffer
*/
public FastByteArrayOutputStream(byte[] pBuffer) {
super(0); // Don't allocate array
buf = pBuffer;
count = pBuffer.length;
}
@Override
public void write(byte pBytes[], int pOffset, int pLength) {
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
throw new IndexOutOfBoundsException();
}
else if (pLength == 0) {
return;
}
int newCount = count + pLength;
growIfNeeded(newCount);
System.arraycopy(pBytes, pOffset, buf, count, pLength);
count = newCount;
}
@Override
public void write(int pByte) {
int newCount = count + 1;
growIfNeeded(newCount);
buf[count] = (byte) pByte;
count = newCount;
}
private void growIfNeeded(int pNewCount) {
if (pNewCount > buf.length) {
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
byte newBuf[] = new byte[newSize];
System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf;
}
}
// Non-synchronized version of writeTo
@Override
public void writeTo(OutputStream pOut) throws IOException {
pOut.write(buf, 0, count);
}
// Non-synchronized version of toByteArray
@Override
public byte[] toByteArray() {
byte newBuf[] = new byte[count];
System.arraycopy(buf, 0, newBuf, 0, count);
return newBuf;
}
/**
* Creates a {@code ByteArrayInputStream} that reads directly from this
* {@code FastByteArrayOutputStream}'s byte buffer.
* The buffer is not cloned, for maximum performance.
* <p/>
* Note that care needs to be taken to avoid writes to
* this output stream after the input stream is created.
* Failing to do so, may result in unpredictable behaviour.
*
* @return a new {@code ByteArrayInputStream}, reading from this stream's buffer.
*/
public ByteArrayInputStream createInputStream() {
return new ByteArrayInputStream(buf, 0, count);
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version
* also has a constructor that lets you create a stream with initial content.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: FastByteArrayOutputStream.java#2 $
*/
// TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block
public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
/** Max grow size (unless if writing more than this amount of bytes) */
protected int maxGrowSize = 1024 * 1024; // 1 MB
/**
* Creates a {@code ByteArrayOutputStream} with the given initial buffer
* size.
*
* @param pSize initial buffer size
*/
public FastByteArrayOutputStream(int pSize) {
super(pSize);
}
/**
* Creates a {@code ByteArrayOutputStream} with the given initial content.
* <p>
* Note that the buffer is not cloned, for maximum performance.
* </p>
*
* @param pBuffer initial buffer
*/
public FastByteArrayOutputStream(byte[] pBuffer) {
super(0); // Don't allocate array
buf = pBuffer;
count = pBuffer.length;
}
@Override
public void write(byte pBytes[], int pOffset, int pLength) {
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
throw new IndexOutOfBoundsException();
}
else if (pLength == 0) {
return;
}
int newCount = count + pLength;
growIfNeeded(newCount);
System.arraycopy(pBytes, pOffset, buf, count, pLength);
count = newCount;
}
@Override
public void write(int pByte) {
int newCount = count + 1;
growIfNeeded(newCount);
buf[count] = (byte) pByte;
count = newCount;
}
private void growIfNeeded(int pNewCount) {
if (pNewCount > buf.length) {
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
byte newBuf[] = new byte[newSize];
System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf;
}
}
// Non-synchronized version of writeTo
@Override
public void writeTo(OutputStream pOut) throws IOException {
pOut.write(buf, 0, count);
}
// Non-synchronized version of toByteArray
@Override
public byte[] toByteArray() {
byte newBuf[] = new byte[count];
System.arraycopy(buf, 0, newBuf, 0, count);
return newBuf;
}
/**
* Creates a {@code ByteArrayInputStream} that reads directly from this
* {@code FastByteArrayOutputStream}'s byte buffer.
* The buffer is not cloned, for maximum performance.
* <p>
* Note that care needs to be taken to avoid writes to
* this output stream after the input stream is created.
* Failing to do so, may result in unpredictable behaviour.
* </p>
*
* @return a new {@code ByteArrayInputStream}, reading from this stream's buffer.
*/
public ByteArrayInputStream createInputStream() {
return new ByteArrayInputStream(buf, 0, count);
}
}

View File

@@ -1,240 +1,241 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate;
import java.io.*;
/**
* A {@code SeekableInputStream} implementation that caches data in a temporary {@code File}.
* <p/>
* Temporary files are created as specified in {@link File#createTempFile(String, String, java.io.File)}.
*
* @see MemoryCacheSeekableStream
* @see FileSeekableStream
*
* @see File#createTempFile(String, String)
* @see RandomAccessFile
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FileCacheSeekableStream.java#5 $
*/
public final class FileCacheSeekableStream extends AbstractCachedSeekableStream {
private byte[] buffer;
/**
* Creates a {@code FileCacheSeekableStream} reading from the given
* {@code InputStream}. Data will be cached in a temporary file.
*
* @param pStream the {@code InputStream} to read from
*
* @throws IOException if the temporary file cannot be created,
* or cannot be opened for random access.
*/
public FileCacheSeekableStream(final InputStream pStream) throws IOException {
this(pStream, "iocache", null);
}
/**
* Creates a {@code FileCacheSeekableStream} reading from the given
* {@code InputStream}. Data will be cached in a temporary file, with
* the given base name.
*
* @param pStream the {@code InputStream} to read from
* @param pTempBaseName optional base name for the temporary file
*
* @throws IOException if the temporary file cannot be created,
* or cannot be opened for random access.
*/
public FileCacheSeekableStream(final InputStream pStream, final String pTempBaseName) throws IOException {
this(pStream, pTempBaseName, null);
}
/**
* Creates a {@code FileCacheSeekableStream} reading from the given
* {@code InputStream}. Data will be cached in a temporary file, with
* the given base name, in the given directory
*
* @param pStream the {@code InputStream} to read from
* @param pTempBaseName optional base name for the temporary file
* @param pTempDir optional temp directory
*
* @throws IOException if the temporary file cannot be created,
* or cannot be opened for random access.
*/
public FileCacheSeekableStream(final InputStream pStream, final String pTempBaseName, final File pTempDir) throws IOException {
// NOTE: We do validation BEFORE we create temp file, to avoid orphan files
this(Validate.notNull(pStream, "stream"), createTempFile(pTempBaseName, pTempDir));
}
/*protected*/ static File createTempFile(String pTempBaseName, File pTempDir) throws IOException {
Validate.notNull(pTempBaseName, "tempBaseName");
File file = File.createTempFile(pTempBaseName, null, pTempDir);
file.deleteOnExit();
return file;
}
// TODO: Consider exposing this for external use
/*protected*/ FileCacheSeekableStream(final InputStream pStream, final File pFile) throws FileNotFoundException {
super(pStream, new FileCache(pFile));
// TODO: Allow for custom buffer sizes?
buffer = new byte[1024];
}
public final boolean isCachedMemory() {
return false;
}
public final boolean isCachedFile() {
return true;
}
@Override
protected void closeImpl() throws IOException {
// TODO: Close cache file
super.closeImpl();
buffer = null;
}
@Override
public int read() throws IOException {
checkOpen();
int read;
if (position == streamPosition) {
// Read ahead into buffer, for performance
read = readAhead(buffer, 0, buffer.length);
if (read >= 0) {
read = buffer[0] & 0xff;
}
//System.out.println("Read 1 byte from stream: " + Integer.toHexString(read & 0xff));
}
else {
// ..or read byte from the cache
syncPosition();
read = getCache().read();
//System.out.println("Read 1 byte from cache: " + Integer.toHexString(read & 0xff));
}
// TODO: This field is not REALLY considered accessible.. :-P
if (read != -1) {
position++;
}
return read;
}
@Override
public int read(byte[] pBytes, int pOffset, int pLength) throws IOException {
checkOpen();
int length;
if (position == streamPosition) {
// Read bytes from the stream
length = readAhead(pBytes, pOffset, pLength);
//System.out.println("Read " + length + " byte from stream");
}
else {
// ...or read bytes from the cache
syncPosition();
length = getCache().read(pBytes, pOffset, (int) Math.min(pLength, streamPosition - position));
//System.out.println("Read " + length + " byte from cache");
}
// TODO: This field is not REALLY considered accessible.. :-P
if (length > 0) {
position += length;
}
return length;
}
private int readAhead(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
int length;
length = stream.read(pBytes, pOffset, pLength);
if (length > 0) {
streamPosition += length;
getCache().write(pBytes, pOffset, length);
}
return length;
}
// TODO: We need to close the cache file!!! Otherwise we are leaking file descriptors
final static class FileCache extends StreamCache {
private RandomAccessFile cacheFile;
public FileCache(final File pFile) throws FileNotFoundException {
Validate.notNull(pFile, "file");
cacheFile = new RandomAccessFile(pFile, "rw");
}
public void write(final int pByte) throws IOException {
cacheFile.write(pByte);
}
@Override
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
cacheFile.write(pBuffer, pOffset, pLength);
}
public int read() throws IOException {
return cacheFile.read();
}
@Override
public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
return cacheFile.read(pBuffer, pOffset, pLength);
}
public void seek(final long pPosition) throws IOException {
cacheFile.seek(pPosition);
}
public long getPosition() throws IOException {
return cacheFile.getFilePointer();
}
@Override
void close() throws IOException {
cacheFile.close();
}
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate;
import java.io.*;
/**
* A {@code SeekableInputStream} implementation that caches data in a temporary {@code File}.
* <p>
* Temporary files are created as specified in {@link File#createTempFile(String, String, java.io.File)}.
* </p>
*
* @see MemoryCacheSeekableStream
* @see FileSeekableStream
*
* @see File#createTempFile(String, String)
* @see RandomAccessFile
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FileCacheSeekableStream.java#5 $
*/
public final class FileCacheSeekableStream extends AbstractCachedSeekableStream {
private byte[] buffer;
/**
* Creates a {@code FileCacheSeekableStream} reading from the given
* {@code InputStream}. Data will be cached in a temporary file.
*
* @param pStream the {@code InputStream} to read from
*
* @throws IOException if the temporary file cannot be created,
* or cannot be opened for random access.
*/
public FileCacheSeekableStream(final InputStream pStream) throws IOException {
this(pStream, "iocache", null);
}
/**
* Creates a {@code FileCacheSeekableStream} reading from the given
* {@code InputStream}. Data will be cached in a temporary file, with
* the given base name.
*
* @param pStream the {@code InputStream} to read from
* @param pTempBaseName optional base name for the temporary file
*
* @throws IOException if the temporary file cannot be created,
* or cannot be opened for random access.
*/
public FileCacheSeekableStream(final InputStream pStream, final String pTempBaseName) throws IOException {
this(pStream, pTempBaseName, null);
}
/**
* Creates a {@code FileCacheSeekableStream} reading from the given
* {@code InputStream}. Data will be cached in a temporary file, with
* the given base name, in the given directory
*
* @param pStream the {@code InputStream} to read from
* @param pTempBaseName optional base name for the temporary file
* @param pTempDir optional temp directory
*
* @throws IOException if the temporary file cannot be created,
* or cannot be opened for random access.
*/
public FileCacheSeekableStream(final InputStream pStream, final String pTempBaseName, final File pTempDir) throws IOException {
// NOTE: We do validation BEFORE we create temp file, to avoid orphan files
this(Validate.notNull(pStream, "stream"), createTempFile(pTempBaseName, pTempDir));
}
/*protected*/ static File createTempFile(String pTempBaseName, File pTempDir) throws IOException {
Validate.notNull(pTempBaseName, "tempBaseName");
File file = File.createTempFile(pTempBaseName, null, pTempDir);
file.deleteOnExit();
return file;
}
// TODO: Consider exposing this for external use
/*protected*/ FileCacheSeekableStream(final InputStream pStream, final File pFile) throws FileNotFoundException {
super(pStream, new FileCache(pFile));
// TODO: Allow for custom buffer sizes?
buffer = new byte[1024];
}
public final boolean isCachedMemory() {
return false;
}
public final boolean isCachedFile() {
return true;
}
@Override
protected void closeImpl() throws IOException {
// TODO: Close cache file
super.closeImpl();
buffer = null;
}
@Override
public int read() throws IOException {
checkOpen();
int read;
if (position == streamPosition) {
// Read ahead into buffer, for performance
read = readAhead(buffer, 0, buffer.length);
if (read >= 0) {
read = buffer[0] & 0xff;
}
//System.out.println("Read 1 byte from stream: " + Integer.toHexString(read & 0xff));
}
else {
// ..or read byte from the cache
syncPosition();
read = getCache().read();
//System.out.println("Read 1 byte from cache: " + Integer.toHexString(read & 0xff));
}
// TODO: This field is not REALLY considered accessible.. :-P
if (read != -1) {
position++;
}
return read;
}
@Override
public int read(byte[] pBytes, int pOffset, int pLength) throws IOException {
checkOpen();
int length;
if (position == streamPosition) {
// Read bytes from the stream
length = readAhead(pBytes, pOffset, pLength);
//System.out.println("Read " + length + " byte from stream");
}
else {
// ...or read bytes from the cache
syncPosition();
length = getCache().read(pBytes, pOffset, (int) Math.min(pLength, streamPosition - position));
//System.out.println("Read " + length + " byte from cache");
}
// TODO: This field is not REALLY considered accessible.. :-P
if (length > 0) {
position += length;
}
return length;
}
private int readAhead(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
int length;
length = stream.read(pBytes, pOffset, pLength);
if (length > 0) {
streamPosition += length;
getCache().write(pBytes, pOffset, length);
}
return length;
}
// TODO: We need to close the cache file!!! Otherwise we are leaking file descriptors
final static class FileCache extends StreamCache {
private RandomAccessFile cacheFile;
public FileCache(final File pFile) throws FileNotFoundException {
Validate.notNull(pFile, "file");
cacheFile = new RandomAccessFile(pFile, "rw");
}
public void write(final int pByte) throws IOException {
cacheFile.write(pByte);
}
@Override
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
cacheFile.write(pBuffer, pOffset, pLength);
}
public int read() throws IOException {
return cacheFile.read();
}
@Override
public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
return cacheFile.read(pBuffer, pOffset, pLength);
}
public void seek(final long pPosition) throws IOException {
cacheFile.seek(pPosition);
}
public long getPosition() throws IOException {
return cacheFile.getFilePointer();
}
@Override
void close() throws IOException {
cacheFile.close();
}
}
}

View File

@@ -1,135 +1,135 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* A {@code SeekableInputStream} implementation that uses random access directly to a {@code File}.
* <p/>
* @see FileCacheSeekableStream
* @see MemoryCacheSeekableStream
* @see RandomAccessFile
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FileSeekableStream.java#4 $
*/
public final class FileSeekableStream extends SeekableInputStream {
// TODO: Figure out why this class is SLOWER than FileCacheSeekableStream in
// my tests..?
final RandomAccessFile mRandomAccess;
/**
* Creates a {@code FileSeekableStream} that reads from the given
* {@code File}.
*
* @param pInput file to read from
* @throws FileNotFoundException if {@code pInput} does not exist
*/
public FileSeekableStream(final File pInput) throws FileNotFoundException {
this(new RandomAccessFile(pInput, "r"));
}
/**
* Creates a {@code FileSeekableStream} that reads from the given file.
* The {@code RandomAccessFile} needs only to be open in read
* ({@code "r"}) mode.
*
* @param pInput file to read from
*/
public FileSeekableStream(final RandomAccessFile pInput) {
mRandomAccess = pInput;
}
/// Seekable
public boolean isCached() {
return false;
}
public boolean isCachedFile() {
return false;
}
public boolean isCachedMemory() {
return false;
}
/// InputStream
@Override
public int available() throws IOException {
long length = mRandomAccess.length() - position;
return length > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) length;
}
public void closeImpl() throws IOException {
mRandomAccess.close();
}
public int read() throws IOException {
checkOpen();
int read = mRandomAccess.read();
if (read >= 0) {
position++;
}
return read;
}
@Override
public int read(byte pBytes[], int pOffset, int pLength) throws IOException {
checkOpen();
int read = mRandomAccess.read(pBytes, pOffset, pLength);
if (read > 0) {
position += read;
}
return read;
}
/**
* Does nothing, as we don't really do any caching here.
*
* @param pPosition the position to flush to
*/
protected void flushBeforeImpl(long pPosition) {
}
protected void seekImpl(long pPosition) throws IOException {
mRandomAccess.seek(pPosition);
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* A {@code SeekableInputStream} implementation that uses random access directly to a {@code File}.
* @see FileCacheSeekableStream
* @see MemoryCacheSeekableStream
* @see RandomAccessFile
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FileSeekableStream.java#4 $
*/
public final class FileSeekableStream extends SeekableInputStream {
// TODO: Figure out why this class is SLOWER than FileCacheSeekableStream in
// my tests..?
final RandomAccessFile mRandomAccess;
/**
* Creates a {@code FileSeekableStream} that reads from the given
* {@code File}.
*
* @param pInput file to read from
* @throws FileNotFoundException if {@code pInput} does not exist
*/
public FileSeekableStream(final File pInput) throws FileNotFoundException {
this(new RandomAccessFile(pInput, "r"));
}
/**
* Creates a {@code FileSeekableStream} that reads from the given file.
* The {@code RandomAccessFile} needs only to be open in read
* ({@code "r"}) mode.
*
* @param pInput file to read from
*/
public FileSeekableStream(final RandomAccessFile pInput) {
mRandomAccess = pInput;
}
/// Seekable
public boolean isCached() {
return false;
}
public boolean isCachedFile() {
return false;
}
public boolean isCachedMemory() {
return false;
}
/// InputStream
@Override
public int available() throws IOException {
long length = mRandomAccess.length() - position;
return length > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) length;
}
public void closeImpl() throws IOException {
mRandomAccess.close();
}
public int read() throws IOException {
checkOpen();
int read = mRandomAccess.read();
if (read >= 0) {
position++;
}
return read;
}
@Override
public int read(byte pBytes[], int pOffset, int pLength) throws IOException {
checkOpen();
int read = mRandomAccess.read(pBytes, pOffset, pLength);
if (read > 0) {
position += read;
}
return read;
}
/**
* Does nothing, as we don't really do any caching here.
*
* @param pPosition the position to flush to
*/
protected void flushBeforeImpl(long pPosition) {
}
protected void seekImpl(long pPosition) throws IOException {
mRandomAccess.seek(pPosition);
}
}

View File

@@ -1,103 +1,102 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* FileSystem
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: FileSystem.java#1 $
*/
abstract class FileSystem {
abstract long getFreeSpace(File pPath);
abstract long getTotalSpace(File pPath);
abstract String getName();
static BufferedReader exec(String[] pArgs) throws IOException {
Process cmd = Runtime.getRuntime().exec(pArgs);
return new BufferedReader(new InputStreamReader(cmd.getInputStream()));
}
static FileSystem get() {
String os = System.getProperty("os.name");
//System.out.println("os = " + os);
os = os.toLowerCase();
if (os.contains("windows")) {
return new Win32FileSystem();
}
else if (os.contains("linux") ||
os.contains("sun os") ||
os.contains("sunos") ||
os.contains("solaris") ||
os.contains("mpe/ix") ||
os.contains("hp-ux") ||
os.contains("aix") ||
os.contains("freebsd") ||
os.contains("irix") ||
os.contains("digital unix") ||
os.contains("unix") ||
os.contains("mac os x")) {
return new UnixFileSystem();
}
else {
return new UnknownFileSystem(os);
}
}
private static class UnknownFileSystem extends FileSystem {
private final String osName;
UnknownFileSystem(String pOSName) {
osName = pOSName;
}
long getFreeSpace(File pPath) {
return 0l;
}
long getTotalSpace(File pPath) {
return 0l;
}
String getName() {
return "Unknown (" + osName + ")";
}
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* FileSystem
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: FileSystem.java#1 $
*/
abstract class FileSystem {
abstract long getFreeSpace(File pPath);
abstract long getTotalSpace(File pPath);
abstract String getName();
static BufferedReader exec(String[] pArgs) throws IOException {
Process cmd = Runtime.getRuntime().exec(pArgs);
return new BufferedReader(new InputStreamReader(cmd.getInputStream()));
}
static FileSystem get() {
String os = System.getProperty("os.name");
//System.out.println("os = " + os);
os = os.toLowerCase();
if (os.contains("windows")) {
return new Win32FileSystem();
}
else if (os.contains("linux") ||
os.contains("sun os") ||
os.contains("sunos") ||
os.contains("solaris") ||
os.contains("mpe/ix") ||
os.contains("hp-ux") ||
os.contains("aix") ||
os.contains("freebsd") ||
os.contains("irix") ||
os.contains("digital unix") ||
os.contains("unix") ||
os.contains("mac os x")) {
return new UnixFileSystem();
}
else {
return new UnknownFileSystem(os);
}
}
private static class UnknownFileSystem extends FileSystem {
private final String osName;
UnknownFileSystem(String pOSName) {
osName = pOSName;
}
long getFreeSpace(File pPath) {
return 0l;
}
long getTotalSpace(File pPath) {
return 0l;
}
String getName() {
return "Unknown (" + osName + ")";
}
}
}

View File

@@ -1,244 +1,249 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.util.regex.WildcardStringParser;
import java.io.File;
import java.io.FilenameFilter;
/**
* A Java Bean used for approving file names which are to be included in a
* {@code java.io.File} listing.
* The mask is given as a well-known DOS filename format, with '*' and '?' as
* wildcards.
* All other characters counts as ordinary characters.
* <p/>
* The file name masks are used as a filter input and is given to the class via
* the string array property:<br>
* <dd>{@code filenameMasksForInclusion} - Filename mask for exclusion of
* files (default if both properties are defined)
* <dd>{@code filenameMasksForExclusion} - Filename mask for exclusion of
* files.
* <p/>
* A recommended way of doing this is by referencing to the component which uses
* this class for file listing. In this way all properties are set in the same
* component and this utility component is kept in the background with only
* initial configuration necessary.
*
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
* @see File#list(java.io.FilenameFilter) java.io.File.list
* @see FilenameFilter java.io.FilenameFilter
* @see WildcardStringParser
* @deprecated
*/
public class FilenameMaskFilter implements FilenameFilter {
// TODO: Rewrite to use regexp, or create new class
// Members
private String[] filenameMasksForInclusion;
private String[] filenameMasksForExclusion;
private boolean inclusion = true;
/**
* Creates a {@code FilenameMaskFilter}
*/
public FilenameMaskFilter() {
}
/**
* Creates a {@code FilenameMaskFilter}
*
* @param pFilenameMask the filename mask
*/
public FilenameMaskFilter(final String pFilenameMask) {
String[] filenameMask = {pFilenameMask};
setFilenameMasksForInclusion(filenameMask);
}
/**
* Creates a {@code FilenameMaskFilter}
*
* @param pFilenameMasks the filename masks
*/
public FilenameMaskFilter(final String[] pFilenameMasks) {
this(pFilenameMasks, false);
}
/**
* Creates a {@code FilenameMaskFilter}
*
* @param pFilenameMask the filename masks
* @param pExclusion if {@code true}, the masks will be excluded
*/
public FilenameMaskFilter(final String pFilenameMask, final boolean pExclusion) {
String[] filenameMask = {pFilenameMask};
if (pExclusion) {
setFilenameMasksForExclusion(filenameMask);
}
else {
setFilenameMasksForInclusion(filenameMask);
}
}
/**
* Creates a {@code FilenameMaskFilter}
*
* @param pFilenameMasks the filename masks
* @param pExclusion if {@code true}, the masks will be excluded
*/
public FilenameMaskFilter(final String[] pFilenameMasks, final boolean pExclusion) {
if (pExclusion) {
setFilenameMasksForExclusion(pFilenameMasks);
}
else {
setFilenameMasksForInclusion(pFilenameMasks);
}
}
/**
*
* @param pFilenameMasksForInclusion the filename masks to include
*/
public void setFilenameMasksForInclusion(String[] pFilenameMasksForInclusion) {
filenameMasksForInclusion = pFilenameMasksForInclusion;
}
/**
* @return the current inclusion masks
*/
public String[] getFilenameMasksForInclusion() {
return filenameMasksForInclusion.clone();
}
/**
* @param pFilenameMasksForExclusion the filename masks to exclude
*/
public void setFilenameMasksForExclusion(String[] pFilenameMasksForExclusion) {
filenameMasksForExclusion = pFilenameMasksForExclusion;
inclusion = false;
}
/**
* @return the current exclusion masks
*/
public String[] getFilenameMasksForExclusion() {
return filenameMasksForExclusion.clone();
}
/**
* This method implements the {@code java.io.FilenameFilter} interface.
*
* @param pDir the directory in which the file was found.
* @param pName the name of the file.
* @return {@code true} if the file {@code pName} should be included in the file
* list; {@code false} otherwise.
*/
public boolean accept(File pDir, String pName) {
WildcardStringParser parser;
// Check each filename string mask whether the file is to be accepted
if (inclusion) { // Inclusion
for (String mask : filenameMasksForInclusion) {
parser = new WildcardStringParser(mask);
if (parser.parseString(pName)) {
// The filename was accepted by the filename masks provided
// - include it in filename list
return true;
}
}
// The filename not was accepted by any of the filename masks
// provided - NOT to be included in the filename list
return false;
}
else {
// Exclusion
for (String mask : filenameMasksForExclusion) {
parser = new WildcardStringParser(mask);
if (parser.parseString(pName)) {
// The filename was accepted by the filename masks provided
// - NOT to be included in the filename list
return false;
}
}
// The filename was not accepted by any of the filename masks
// provided - include it in filename list
return true;
}
}
/**
* @return a string representation for debug purposes
*/
public String toString() {
StringBuilder retVal = new StringBuilder();
int i;
if (inclusion) {
// Inclusion
if (filenameMasksForInclusion == null) {
retVal.append("No filename masks set - property filenameMasksForInclusion is null!");
}
else {
retVal.append(filenameMasksForInclusion.length);
retVal.append(" filename mask(s) - ");
for (i = 0; i < filenameMasksForInclusion.length; i++) {
retVal.append("\"");
retVal.append(filenameMasksForInclusion[i]);
retVal.append("\", \"");
}
}
}
else {
// Exclusion
if (filenameMasksForExclusion == null) {
retVal.append("No filename masks set - property filenameMasksForExclusion is null!");
}
else {
retVal.append(filenameMasksForExclusion.length);
retVal.append(" exclusion filename mask(s) - ");
for (i = 0; i < filenameMasksForExclusion.length; i++) {
retVal.append("\"");
retVal.append(filenameMasksForExclusion[i]);
retVal.append("\", \"");
}
}
}
return retVal.toString();
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.util.regex.WildcardStringParser;
import java.io.File;
import java.io.FilenameFilter;
/**
* A Java Bean used for approving file names which are to be included in a
* {@code java.io.File} listing.
* The mask is given as a well-known DOS filename format, with '*' and '?' as
* wildcards.
* All other characters counts as ordinary characters.
* <p>
* The file name masks are used as a filter input and is given to the class via
* the string array property:
* </p>
* <dl>
* <dt>{@code filenameMasksForInclusion}</dt>
* <dd>Filename mask for exclusion of
* files (default if both properties are defined).</dd>
* <dt>{@code filenameMasksForExclusion}</dt>
* <dd>Filename mask for exclusion of files.</dd>
* </dl>
* <p>
* A recommended way of doing this is by referencing to the component which uses
* this class for file listing. In this way all properties are set in the same
* component and this utility component is kept in the background with only
* initial configuration necessary.
* </p>
*
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
* @see File#list(java.io.FilenameFilter) java.io.File.list
* @see FilenameFilter java.io.FilenameFilter
* @see WildcardStringParser
* @deprecated
*/
public class FilenameMaskFilter implements FilenameFilter {
// TODO: Rewrite to use regexp, or create new class
// Members
private String[] filenameMasksForInclusion;
private String[] filenameMasksForExclusion;
private boolean inclusion = true;
/**
* Creates a {@code FilenameMaskFilter}
*/
public FilenameMaskFilter() {
}
/**
* Creates a {@code FilenameMaskFilter}
*
* @param pFilenameMask the filename mask
*/
public FilenameMaskFilter(final String pFilenameMask) {
String[] filenameMask = {pFilenameMask};
setFilenameMasksForInclusion(filenameMask);
}
/**
* Creates a {@code FilenameMaskFilter}
*
* @param pFilenameMasks the filename masks
*/
public FilenameMaskFilter(final String[] pFilenameMasks) {
this(pFilenameMasks, false);
}
/**
* Creates a {@code FilenameMaskFilter}
*
* @param pFilenameMask the filename masks
* @param pExclusion if {@code true}, the masks will be excluded
*/
public FilenameMaskFilter(final String pFilenameMask, final boolean pExclusion) {
String[] filenameMask = {pFilenameMask};
if (pExclusion) {
setFilenameMasksForExclusion(filenameMask);
}
else {
setFilenameMasksForInclusion(filenameMask);
}
}
/**
* Creates a {@code FilenameMaskFilter}
*
* @param pFilenameMasks the filename masks
* @param pExclusion if {@code true}, the masks will be excluded
*/
public FilenameMaskFilter(final String[] pFilenameMasks, final boolean pExclusion) {
if (pExclusion) {
setFilenameMasksForExclusion(pFilenameMasks);
}
else {
setFilenameMasksForInclusion(pFilenameMasks);
}
}
/**
*
* @param pFilenameMasksForInclusion the filename masks to include
*/
public void setFilenameMasksForInclusion(String[] pFilenameMasksForInclusion) {
filenameMasksForInclusion = pFilenameMasksForInclusion;
}
/**
* @return the current inclusion masks
*/
public String[] getFilenameMasksForInclusion() {
return filenameMasksForInclusion.clone();
}
/**
* @param pFilenameMasksForExclusion the filename masks to exclude
*/
public void setFilenameMasksForExclusion(String[] pFilenameMasksForExclusion) {
filenameMasksForExclusion = pFilenameMasksForExclusion;
inclusion = false;
}
/**
* @return the current exclusion masks
*/
public String[] getFilenameMasksForExclusion() {
return filenameMasksForExclusion.clone();
}
/**
* This method implements the {@code java.io.FilenameFilter} interface.
*
* @param pDir the directory in which the file was found.
* @param pName the name of the file.
* @return {@code true} if the file {@code pName} should be included in the file
* list; {@code false} otherwise.
*/
public boolean accept(File pDir, String pName) {
WildcardStringParser parser;
// Check each filename string mask whether the file is to be accepted
if (inclusion) { // Inclusion
for (String mask : filenameMasksForInclusion) {
parser = new WildcardStringParser(mask);
if (parser.parseString(pName)) {
// The filename was accepted by the filename masks provided
// - include it in filename list
return true;
}
}
// The filename not was accepted by any of the filename masks
// provided - NOT to be included in the filename list
return false;
}
else {
// Exclusion
for (String mask : filenameMasksForExclusion) {
parser = new WildcardStringParser(mask);
if (parser.parseString(pName)) {
// The filename was accepted by the filename masks provided
// - NOT to be included in the filename list
return false;
}
}
// The filename was not accepted by any of the filename masks
// provided - include it in filename list
return true;
}
}
/**
* @return a string representation for debug purposes
*/
public String toString() {
StringBuilder retVal = new StringBuilder();
int i;
if (inclusion) {
// Inclusion
if (filenameMasksForInclusion == null) {
retVal.append("No filename masks set - property filenameMasksForInclusion is null!");
}
else {
retVal.append(filenameMasksForInclusion.length);
retVal.append(" filename mask(s) - ");
for (i = 0; i < filenameMasksForInclusion.length; i++) {
retVal.append("\"");
retVal.append(filenameMasksForInclusion[i]);
retVal.append("\", \"");
}
}
}
else {
// Exclusion
if (filenameMasksForExclusion == null) {
retVal.append("No filename masks set - property filenameMasksForExclusion is null!");
}
else {
retVal.append(filenameMasksForExclusion.length);
retVal.append(" exclusion filename mask(s) - ");
for (i = 0; i < filenameMasksForExclusion.length; i++) {
retVal.append("\"");
retVal.append(filenameMasksForExclusion[i]);
retVal.append("\", \"");
}
}
}
return retVal.toString();
}
}

View File

@@ -1,452 +1,449 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* From http://www.cafeaulait.org/books/javaio/ioexamples/index.html:
*
* Please feel free to use any fragment of this code you need in your own work.
* As far as I am concerned, it's in the public domain. No permission is necessary
* or required. Credit is always appreciated if you use a large chunk or base a
* significant product on one of my examples, but that's not required either.
*
* Elliotte Rusty Harold
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate;
import java.io.*;
/**
* A little endian input stream reads two's complement,
* little endian integers, floating point numbers, and characters
* and returns them as Java primitive types.
* <p/>
* The standard {@code java.io.DataInputStream} class
* which this class imitates reads big endian quantities.
* <p/>
* <em>Warning:
* The {@code DataInput} and {@code DataOutput} interfaces
* specifies big endian byte order in their documentation.
* This means that this class is, strictly speaking, not a proper
* implementation. However, I don't see a reason for the these interfaces to
* specify the byte order of their underlying representations.
* </em>
*
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
* @see java.io.DataInputStream
* @see java.io.DataInput
* @see java.io.DataOutput
*
* @author Elliotte Rusty Harold
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version 2
*/
public class LittleEndianDataInputStream extends FilterInputStream implements DataInput {
// TODO: Optimize by reading into a fixed size (8 bytes) buffer instead of individual read operations?
/**
* Creates a new little endian input stream and chains it to the
* input stream specified by the {@code pStream} argument.
*
* @param pStream the underlying input stream.
* @see java.io.FilterInputStream#in
*/
public LittleEndianDataInputStream(final InputStream pStream) {
super(Validate.notNull(pStream, "stream"));
}
/**
* Reads a {@code boolean} from the underlying input stream by
* reading a single byte. If the byte is zero, false is returned.
* If the byte is positive, true is returned.
*
* @return the {@code boolean} value read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public boolean readBoolean() throws IOException {
int b = in.read();
if (b < 0) {
throw new EOFException();
}
return b != 0;
}
/**
* Reads a signed {@code byte} from the underlying input stream
* with value between -128 and 127
*
* @return the {@code byte} value read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public byte readByte() throws IOException {
int b = in.read();
if (b < 0) {
throw new EOFException();
}
return (byte) b;
}
/**
* Reads an unsigned {@code byte} from the underlying
* input stream with value between 0 and 255
*
* @return the {@code byte} value read.
* @throws EOFException if the end of the underlying input
* stream has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public int readUnsignedByte() throws IOException {
int b = in.read();
if (b < 0) {
throw new EOFException();
}
return b;
}
/**
* Reads a two byte signed {@code short} from the underlying
* input stream in little endian order, low byte first.
*
* @return the {@code short} read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public short readShort() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
// only need to test last byte read
// if byte1 is -1 so is byte2
if (byte2 < 0) {
throw new EOFException();
}
return (short) (((byte2 << 24) >>> 16) | (byte1 << 24) >>> 24);
}
/**
* Reads a two byte unsigned {@code short} from the underlying
* input stream in little endian order, low byte first.
*
* @return the int value of the unsigned short read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public int readUnsignedShort() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
if (byte2 < 0) {
throw new EOFException();
}
return (byte2 << 8) + byte1;
}
/**
* Reads a two byte Unicode {@code char} from the underlying
* input stream in little endian order, low byte first.
*
* @return the int value of the unsigned short read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public char readChar() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
if (byte2 < 0) {
throw new EOFException();
}
return (char) (((byte2 << 24) >>> 16) | ((byte1 << 24) >>> 24));
}
/**
* Reads a four byte signed {@code int} from the underlying
* input stream in little endian order, low byte first.
*
* @return the {@code int} read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public int readInt() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
int byte3 = in.read();
int byte4 = in.read();
if (byte4 < 0) {
throw new EOFException();
}
return (byte4 << 24) | ((byte3 << 24) >>> 8)
| ((byte2 << 24) >>> 16) | ((byte1 << 24) >>> 24);
}
/**
* Reads an eight byte signed {@code int} from the underlying
* input stream in little endian order, low byte first.
*
* @return the {@code int} read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public long readLong() throws IOException {
long byte1 = in.read();
long byte2 = in.read();
long byte3 = in.read();
long byte4 = in.read();
long byte5 = in.read();
long byte6 = in.read();
long byte7 = in.read();
long byte8 = in.read();
if (byte8 < 0) {
throw new EOFException();
}
return (byte8 << 56) | ((byte7 << 56) >>> 8)
| ((byte6 << 56) >>> 16) | ((byte5 << 56) >>> 24)
| ((byte4 << 56) >>> 32) | ((byte3 << 56) >>> 40)
| ((byte2 << 56) >>> 48) | ((byte1 << 56) >>> 56);
}
/**
* Reads a string of no more than 65,535 characters
* from the underlying input stream using UTF-8
* encoding. This method first reads a two byte short
* in <b>big</b> endian order as required by the
* UTF-8 specification. This gives the number of bytes in
* the UTF-8 encoded version of the string.
* Next this many bytes are read and decoded as UTF-8
* encoded characters.
*
* @return the decoded string
* @throws UTFDataFormatException if the string cannot be decoded
* @throws IOException if the underlying stream throws an IOException.
*/
public String readUTF() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
if (byte2 < 0) {
throw new EOFException();
}
int numbytes = (byte1 << 8) + byte2;
char result[] = new char[numbytes];
int numread = 0;
int numchars = 0;
while (numread < numbytes) {
int c1 = readUnsignedByte();
int c2, c3;
// The first four bits of c1 determine how many bytes are in this char
int test = c1 >> 4;
if (test < 8) { // one byte
numread++;
result[numchars++] = (char) c1;
}
else if (test == 12 || test == 13) { // two bytes
numread += 2;
if (numread > numbytes) {
throw new UTFDataFormatException();
}
c2 = readUnsignedByte();
if ((c2 & 0xC0) != 0x80) {
throw new UTFDataFormatException();
}
result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
}
else if (test == 14) { // three bytes
numread += 3;
if (numread > numbytes) {
throw new UTFDataFormatException();
}
c2 = readUnsignedByte();
c3 = readUnsignedByte();
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
throw new UTFDataFormatException();
}
result[numchars++] = (char) (((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
}
else { // malformed
throw new UTFDataFormatException();
}
} // end while
return new String(result, 0, numchars);
}
/**
* @return the next eight bytes of this input stream, interpreted as a
* little endian {@code double}.
* @throws EOFException if end of stream occurs before eight bytes
* have been read.
* @throws IOException if an I/O error occurs.
*/
public final double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
/**
* @return the next four bytes of this input stream, interpreted as a
* little endian {@code int}.
* @throws EOFException if end of stream occurs before four bytes
* have been read.
* @throws IOException if an I/O error occurs.
*/
public final float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
/**
* See the general contract of the {@code skipBytes}
* method of {@code DataInput}.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @param pLength the number of bytes to be skipped.
* @return the actual number of bytes skipped.
* @exception IOException if an I/O error occurs.
*/
public final int skipBytes(int pLength) throws IOException {
// NOTE: There was probably a bug in ERH's original code here, as skip
// never returns -1, but returns 0 if no more bytes can be skipped...
int total = 0;
int skipped;
while ((total < pLength) && ((skipped = (int) in.skip(pLength - total)) > 0)) {
total += skipped;
}
return total;
}
/**
* See the general contract of the {@code readFully}
* method of {@code DataInput}.
* <p/>
* Bytes
* for this operation are read from the contained
* input stream.
*
* @param pBytes the buffer into which the data is read.
* @throws EOFException if this input stream reaches the end before
* reading all the bytes.
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
public final void readFully(byte pBytes[]) throws IOException {
readFully(pBytes, 0, pBytes.length);
}
/**
* See the general contract of the {@code readFully}
* method of {@code DataInput}.
* <p/>
* Bytes
* for this operation are read from the contained
* input stream.
*
* @param pBytes the buffer into which the data is read.
* @param pOffset the start offset of the data.
* @param pLength the number of bytes to read.
* @throws EOFException if this input stream reaches the end before
* reading all the bytes.
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
public final void readFully(byte pBytes[], int pOffset, int pLength) throws IOException {
if (pLength < 0) {
throw new IndexOutOfBoundsException();
}
int count = 0;
while (count < pLength) {
int read = in.read(pBytes, pOffset + count, pLength - count);
if (read < 0) {
throw new EOFException();
}
count += read;
}
}
/**
* See the general contract of the {@code readLine}
* method of {@code DataInput}.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @deprecated This method does not properly convert bytes to characters.
*
* @return the next line of text from this input stream.
* @exception IOException if an I/O error occurs.
* @see java.io.BufferedReader#readLine()
* @see java.io.DataInputStream#readLine()
* @noinspection deprecation
*/
public String readLine() throws IOException {
DataInputStream ds = new DataInputStream(in);
return ds.readLine();
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* From http://www.cafeaulait.org/books/javaio/ioexamples/index.html:
*
* Please feel free to use any fragment of this code you need in your own work.
* As far as I am concerned, it's in the public domain. No permission is necessary
* or required. Credit is always appreciated if you use a large chunk or base a
* significant product on one of my examples, but that's not required either.
*
* Elliotte Rusty Harold
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate;
import java.io.*;
/**
* A little endian input stream reads two's complement,
* little endian integers, floating point numbers, and characters
* and returns them as Java primitive types.
* <p>
* The standard {@code java.io.DataInputStream} class
* which this class imitates reads big endian quantities.
* </p>
* <p>
* <em>Warning:
* The {@code DataInput} and {@code DataOutput} interfaces
* specifies big endian byte order in their documentation.
* This means that this class is, strictly speaking, not a proper
* implementation. However, I don't see a reason for the these interfaces to
* specify the byte order of their underlying representations.
* </em>
* </p>
*
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
* @see java.io.DataInputStream
* @see java.io.DataInput
* @see java.io.DataOutput
*
* @author Elliotte Rusty Harold
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version 2
*/
public class LittleEndianDataInputStream extends FilterInputStream implements DataInput {
// TODO: Optimize by reading into a fixed size (8 bytes) buffer instead of individual read operations?
/**
* Creates a new little endian input stream and chains it to the
* input stream specified by the {@code pStream} argument.
*
* @param pStream the underlying input stream.
* @see java.io.FilterInputStream#in
*/
public LittleEndianDataInputStream(final InputStream pStream) {
super(Validate.notNull(pStream, "stream"));
}
/**
* Reads a {@code boolean} from the underlying input stream by
* reading a single byte. If the byte is zero, false is returned.
* If the byte is positive, true is returned.
*
* @return the {@code boolean} value read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public boolean readBoolean() throws IOException {
int b = in.read();
if (b < 0) {
throw new EOFException();
}
return b != 0;
}
/**
* Reads a signed {@code byte} from the underlying input stream
* with value between -128 and 127
*
* @return the {@code byte} value read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public byte readByte() throws IOException {
int b = in.read();
if (b < 0) {
throw new EOFException();
}
return (byte) b;
}
/**
* Reads an unsigned {@code byte} from the underlying
* input stream with value between 0 and 255
*
* @return the {@code byte} value read.
* @throws EOFException if the end of the underlying input
* stream has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public int readUnsignedByte() throws IOException {
int b = in.read();
if (b < 0) {
throw new EOFException();
}
return b;
}
/**
* Reads a two byte signed {@code short} from the underlying
* input stream in little endian order, low byte first.
*
* @return the {@code short} read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public short readShort() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
// only need to test last byte read
// if byte1 is -1 so is byte2
if (byte2 < 0) {
throw new EOFException();
}
return (short) (((byte2 << 24) >>> 16) | (byte1 << 24) >>> 24);
}
/**
* Reads a two byte unsigned {@code short} from the underlying
* input stream in little endian order, low byte first.
*
* @return the int value of the unsigned short read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public int readUnsignedShort() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
if (byte2 < 0) {
throw new EOFException();
}
return (byte2 << 8) + byte1;
}
/**
* Reads a two byte Unicode {@code char} from the underlying
* input stream in little endian order, low byte first.
*
* @return the int value of the unsigned short read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public char readChar() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
if (byte2 < 0) {
throw new EOFException();
}
return (char) (((byte2 << 24) >>> 16) | ((byte1 << 24) >>> 24));
}
/**
* Reads a four byte signed {@code int} from the underlying
* input stream in little endian order, low byte first.
*
* @return the {@code int} read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public int readInt() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
int byte3 = in.read();
int byte4 = in.read();
if (byte4 < 0) {
throw new EOFException();
}
return (byte4 << 24) | ((byte3 << 24) >>> 8)
| ((byte2 << 24) >>> 16) | ((byte1 << 24) >>> 24);
}
/**
* Reads an eight byte signed {@code int} from the underlying
* input stream in little endian order, low byte first.
*
* @return the {@code int} read.
* @throws EOFException if the end of the underlying input stream
* has been reached
* @throws IOException if the underlying stream throws an IOException.
*/
public long readLong() throws IOException {
long byte1 = in.read();
long byte2 = in.read();
long byte3 = in.read();
long byte4 = in.read();
long byte5 = in.read();
long byte6 = in.read();
long byte7 = in.read();
long byte8 = in.read();
if (byte8 < 0) {
throw new EOFException();
}
return (byte8 << 56) | ((byte7 << 56) >>> 8)
| ((byte6 << 56) >>> 16) | ((byte5 << 56) >>> 24)
| ((byte4 << 56) >>> 32) | ((byte3 << 56) >>> 40)
| ((byte2 << 56) >>> 48) | ((byte1 << 56) >>> 56);
}
/**
* Reads a string of no more than 65,535 characters
* from the underlying input stream using UTF-8
* encoding. This method first reads a two byte short
* in <b>big</b> endian order as required by the
* UTF-8 specification. This gives the number of bytes in
* the UTF-8 encoded version of the string.
* Next this many bytes are read and decoded as UTF-8
* encoded characters.
*
* @return the decoded string
* @throws UTFDataFormatException if the string cannot be decoded
* @throws IOException if the underlying stream throws an IOException.
*/
public String readUTF() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
if (byte2 < 0) {
throw new EOFException();
}
int numbytes = (byte1 << 8) + byte2;
char result[] = new char[numbytes];
int numread = 0;
int numchars = 0;
while (numread < numbytes) {
int c1 = readUnsignedByte();
int c2, c3;
// The first four bits of c1 determine how many bytes are in this char
int test = c1 >> 4;
if (test < 8) { // one byte
numread++;
result[numchars++] = (char) c1;
}
else if (test == 12 || test == 13) { // two bytes
numread += 2;
if (numread > numbytes) {
throw new UTFDataFormatException();
}
c2 = readUnsignedByte();
if ((c2 & 0xC0) != 0x80) {
throw new UTFDataFormatException();
}
result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
}
else if (test == 14) { // three bytes
numread += 3;
if (numread > numbytes) {
throw new UTFDataFormatException();
}
c2 = readUnsignedByte();
c3 = readUnsignedByte();
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
throw new UTFDataFormatException();
}
result[numchars++] = (char) (((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
}
else { // malformed
throw new UTFDataFormatException();
}
} // end while
return new String(result, 0, numchars);
}
/**
* @return the next eight bytes of this input stream, interpreted as a
* little endian {@code double}.
* @throws EOFException if end of stream occurs before eight bytes
* have been read.
* @throws IOException if an I/O error occurs.
*/
public final double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
/**
* @return the next four bytes of this input stream, interpreted as a
* little endian {@code int}.
* @throws EOFException if end of stream occurs before four bytes
* have been read.
* @throws IOException if an I/O error occurs.
*/
public final float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
/**
* See the general contract of the {@code skipBytes}
* method of {@code DataInput}.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @param pLength the number of bytes to be skipped.
* @return the actual number of bytes skipped.
* @exception IOException if an I/O error occurs.
*/
public final int skipBytes(int pLength) throws IOException {
// NOTE: There was probably a bug in ERH's original code here, as skip
// never returns -1, but returns 0 if no more bytes can be skipped...
int total = 0;
int skipped;
while ((total < pLength) && ((skipped = (int) in.skip(pLength - total)) > 0)) {
total += skipped;
}
return total;
}
/**
* See the general contract of the {@code readFully} method of {@code DataInput}.
* <p>
* Bytes for this operation are read from the contained input stream.
* </p>
*
* @param pBytes the buffer into which the data is read.
* @throws EOFException if this input stream reaches the end before
* reading all the bytes.
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
public final void readFully(byte pBytes[]) throws IOException {
readFully(pBytes, 0, pBytes.length);
}
/**
* See the general contract of the {@code readFully} method of {@code DataInput}.
* <p>
* Bytes for this operation are read from the contained input stream.
* </p>
*
* @param pBytes the buffer into which the data is read.
* @param pOffset the start offset of the data.
* @param pLength the number of bytes to read.
* @throws EOFException if this input stream reaches the end before
* reading all the bytes.
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
public final void readFully(byte pBytes[], int pOffset, int pLength) throws IOException {
if (pLength < 0) {
throw new IndexOutOfBoundsException();
}
int count = 0;
while (count < pLength) {
int read = in.read(pBytes, pOffset + count, pLength - count);
if (read < 0) {
throw new EOFException();
}
count += read;
}
}
/**
* See the general contract of the {@code readLine}
* method of {@code DataInput}.
* <p>
* Bytes for this operation are read from the contained input stream.
*
* @deprecated This method does not properly convert bytes to characters.
*
* @return the next line of text from this input stream.
* @exception IOException if an I/O error occurs.
* @see java.io.BufferedReader#readLine()
* @see java.io.DataInputStream#readLine()
*/
public String readLine() throws IOException {
DataInputStream ds = new DataInputStream(in);
return ds.readLine();
}
}

View File

@@ -1,340 +1,342 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* From http://www.cafeaulait.org/books/javaio/ioexamples/index.html:
*
* Please feel free to use any fragment of this code you need in your own work.
* As far as I am concerned, it's in the public domain. No permission is necessary
* or required. Credit is always appreciated if you use a large chunk or base a
* significant product on one of my examples, but that's not required either.
*
* Elliotte Rusty Harold
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate;
import java.io.*;
/**
* A little endian output stream writes primitive Java numbers
* and characters to an output stream in a little endian format.
* <p/>
* The standard {@code java.io.DataOutputStream} class which this class
* imitates uses big endian integers.
* <p/>
* <em>Warning:
* The {@code DataInput} and {@code DataOutput} interfaces
* specifies big endian byte order in their documentation.
* This means that this class is, strictly speaking, not a proper
* implementation. However, I don't see a reason for the these interfaces to
* specify the byte order of their underlying representations.
* </em>
*
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
* @see java.io.DataOutputStream
* @see java.io.DataInput
* @see java.io.DataOutput
*
* @author Elliotte Rusty Harold
* @version 1.0.1, 19 May 1999
*/
public class LittleEndianDataOutputStream extends FilterOutputStream implements DataOutput {
/**
* The number of bytes written so far to the little endian output stream.
*/
protected int bytesWritten;
/**
* Creates a new little endian output stream and chains it to the
* output stream specified by the {@code pStream} argument.
*
* @param pStream the underlying output stream.
* @see java.io.FilterOutputStream#out
*/
public LittleEndianDataOutputStream(OutputStream pStream) {
super(Validate.notNull(pStream, "stream"));
}
/**
* Writes the specified byte value to the underlying output stream.
*
* @param pByte the {@code byte} value to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public synchronized void write(int pByte) throws IOException {
out.write(pByte);
bytesWritten++;
}
/**
* Writes {@code pLength} bytes from the specified byte array
* starting at {@code pOffset} to the underlying output stream.
*
* @param pBytes the data.
* @param pOffset the start offset in the data.
* @param pLength the number of bytes to write.
* @throws IOException if the underlying stream throws an IOException.
*/
public synchronized void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
out.write(pBytes, pOffset, pLength);
bytesWritten += pLength;
}
/**
* Writes a {@code boolean} to the underlying output stream as
* a single byte. If the argument is true, the byte value 1 is written.
* If the argument is false, the byte value {@code 0} in written.
*
* @param pBoolean the {@code boolean} value to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeBoolean(boolean pBoolean) throws IOException {
if (pBoolean) {
write(1);
}
else {
write(0);
}
}
/**
* Writes out a {@code byte} to the underlying output stream
*
* @param pByte the {@code byte} value to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeByte(int pByte) throws IOException {
out.write(pByte);
bytesWritten++;
}
/**
* Writes a two byte {@code short} to the underlying output stream in
* little endian order, low byte first.
*
* @param pShort the {@code short} to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeShort(int pShort) throws IOException {
out.write(pShort & 0xFF);
out.write((pShort >>> 8) & 0xFF);
bytesWritten += 2;
}
/**
* Writes a two byte {@code char} to the underlying output stream
* in little endian order, low byte first.
*
* @param pChar the {@code char} value to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeChar(int pChar) throws IOException {
out.write(pChar & 0xFF);
out.write((pChar >>> 8) & 0xFF);
bytesWritten += 2;
}
/**
* Writes a four-byte {@code int} to the underlying output stream
* in little endian order, low byte first, high byte last
*
* @param pInt the {@code int} to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeInt(int pInt) throws IOException {
out.write(pInt & 0xFF);
out.write((pInt >>> 8) & 0xFF);
out.write((pInt >>> 16) & 0xFF);
out.write((pInt >>> 24) & 0xFF);
bytesWritten += 4;
}
/**
* Writes an eight-byte {@code long} to the underlying output stream
* in little endian order, low byte first, high byte last
*
* @param pLong the {@code long} to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeLong(long pLong) throws IOException {
out.write((int) pLong & 0xFF);
out.write((int) (pLong >>> 8) & 0xFF);
out.write((int) (pLong >>> 16) & 0xFF);
out.write((int) (pLong >>> 24) & 0xFF);
out.write((int) (pLong >>> 32) & 0xFF);
out.write((int) (pLong >>> 40) & 0xFF);
out.write((int) (pLong >>> 48) & 0xFF);
out.write((int) (pLong >>> 56) & 0xFF);
bytesWritten += 8;
}
/**
* Writes a 4 byte Java float to the underlying output stream in
* little endian order.
*
* @param f the {@code float} value to be written.
* @throws IOException if an I/O error occurs.
*/
public final void writeFloat(float f) throws IOException {
writeInt(Float.floatToIntBits(f));
}
/**
* Writes an 8 byte Java double to the underlying output stream in
* little endian order.
*
* @param d the {@code double} value to be written.
* @throws IOException if an I/O error occurs.
*/
public final void writeDouble(double d) throws IOException {
writeLong(Double.doubleToLongBits(d));
}
/**
* Writes a string to the underlying output stream as a sequence of
* bytes. Each character is written to the data output stream as
* if by the {@link #writeByte(int)} method.
*
* @param pString the {@code String} value to be written.
* @throws IOException if the underlying stream throws an IOException.
* @see #writeByte(int)
* @see #out
*/
public void writeBytes(String pString) throws IOException {
int length = pString.length();
for (int i = 0; i < length; i++) {
out.write((byte) pString.charAt(i));
}
bytesWritten += length;
}
/**
* Writes a string to the underlying output stream as a sequence of
* characters. Each character is written to the data output stream as
* if by the {@code writeChar} method.
*
* @param pString a {@code String} value to be written.
* @throws IOException if the underlying stream throws an IOException.
* @see #writeChar(int)
* @see #out
*/
public void writeChars(String pString) throws IOException {
int length = pString.length();
for (int i = 0; i < length; i++) {
int c = pString.charAt(i);
out.write(c & 0xFF);
out.write((c >>> 8) & 0xFF);
}
bytesWritten += length * 2;
}
/**
* Writes a string of no more than 65,535 characters
* to the underlying output stream using UTF-8
* encoding. This method first writes a two byte short
* in <b>big</b> endian order as required by the
* UTF-8 specification. This gives the number of bytes in the
* UTF-8 encoded version of the string, not the number of characters
* in the string. Next each character of the string is written
* using the UTF-8 encoding for the character.
*
* @param pString the string to be written.
* @throws UTFDataFormatException if the string is longer than
* 65,535 characters.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeUTF(String pString) throws IOException {
int numchars = pString.length();
int numbytes = 0;
for (int i = 0; i < numchars; i++) {
int c = pString.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
numbytes++;
}
else if (c > 0x07FF) {
numbytes += 3;
}
else {
numbytes += 2;
}
}
if (numbytes > 65535) {
throw new UTFDataFormatException();
}
out.write((numbytes >>> 8) & 0xFF);
out.write(numbytes & 0xFF);
for (int i = 0; i < numchars; i++) {
int c = pString.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
out.write(c);
}
else if (c > 0x07FF) {
out.write(0xE0 | ((c >> 12) & 0x0F));
out.write(0x80 | ((c >> 6) & 0x3F));
out.write(0x80 | (c & 0x3F));
bytesWritten += 2;
}
else {
out.write(0xC0 | ((c >> 6) & 0x1F));
out.write(0x80 | (c & 0x3F));
bytesWritten += 1;
}
}
bytesWritten += numchars + 2;
}
/**
* Returns the number of bytes written to this little endian output stream.
* (This class is not thread-safe with respect to this method. It is
* possible that this number is temporarily less than the actual
* number of bytes written.)
* @return the value of the {@code written} field.
* @see #bytesWritten
*/
public int size() {
return bytesWritten;
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* From http://www.cafeaulait.org/books/javaio/ioexamples/index.html:
*
* Please feel free to use any fragment of this code you need in your own work.
* As far as I am concerned, it's in the public domain. No permission is necessary
* or required. Credit is always appreciated if you use a large chunk or base a
* significant product on one of my examples, but that's not required either.
*
* Elliotte Rusty Harold
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate;
import java.io.*;
/**
* A little endian output stream writes primitive Java numbers
* and characters to an output stream in a little endian format.
* <p>
* The standard {@code java.io.DataOutputStream} class which this class
* imitates uses big endian integers.
* </p>
* <p>
* <em>Warning:
* The {@code DataInput} and {@code DataOutput} interfaces
* specifies big endian byte order in their documentation.
* This means that this class is, strictly speaking, not a proper
* implementation. However, I don't see a reason for the these interfaces to
* specify the byte order of their underlying representations.
* </em>
* <p>
*
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
* @see java.io.DataOutputStream
* @see java.io.DataInput
* @see java.io.DataOutput
*
* @author Elliotte Rusty Harold
* @version 1.0.1, 19 May 1999
*/
public class LittleEndianDataOutputStream extends FilterOutputStream implements DataOutput {
/**
* The number of bytes written so far to the little endian output stream.
*/
protected int bytesWritten;
/**
* Creates a new little endian output stream and chains it to the
* output stream specified by the {@code pStream} argument.
*
* @param pStream the underlying output stream.
* @see java.io.FilterOutputStream#out
*/
public LittleEndianDataOutputStream(OutputStream pStream) {
super(Validate.notNull(pStream, "stream"));
}
/**
* Writes the specified byte value to the underlying output stream.
*
* @param pByte the {@code byte} value to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public synchronized void write(int pByte) throws IOException {
out.write(pByte);
bytesWritten++;
}
/**
* Writes {@code pLength} bytes from the specified byte array
* starting at {@code pOffset} to the underlying output stream.
*
* @param pBytes the data.
* @param pOffset the start offset in the data.
* @param pLength the number of bytes to write.
* @throws IOException if the underlying stream throws an IOException.
*/
public synchronized void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
out.write(pBytes, pOffset, pLength);
bytesWritten += pLength;
}
/**
* Writes a {@code boolean} to the underlying output stream as
* a single byte. If the argument is true, the byte value 1 is written.
* If the argument is false, the byte value {@code 0} in written.
*
* @param pBoolean the {@code boolean} value to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeBoolean(boolean pBoolean) throws IOException {
if (pBoolean) {
write(1);
}
else {
write(0);
}
}
/**
* Writes out a {@code byte} to the underlying output stream
*
* @param pByte the {@code byte} value to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeByte(int pByte) throws IOException {
out.write(pByte);
bytesWritten++;
}
/**
* Writes a two byte {@code short} to the underlying output stream in
* little endian order, low byte first.
*
* @param pShort the {@code short} to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeShort(int pShort) throws IOException {
out.write(pShort & 0xFF);
out.write((pShort >>> 8) & 0xFF);
bytesWritten += 2;
}
/**
* Writes a two byte {@code char} to the underlying output stream
* in little endian order, low byte first.
*
* @param pChar the {@code char} value to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeChar(int pChar) throws IOException {
out.write(pChar & 0xFF);
out.write((pChar >>> 8) & 0xFF);
bytesWritten += 2;
}
/**
* Writes a four-byte {@code int} to the underlying output stream
* in little endian order, low byte first, high byte last
*
* @param pInt the {@code int} to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeInt(int pInt) throws IOException {
out.write(pInt & 0xFF);
out.write((pInt >>> 8) & 0xFF);
out.write((pInt >>> 16) & 0xFF);
out.write((pInt >>> 24) & 0xFF);
bytesWritten += 4;
}
/**
* Writes an eight-byte {@code long} to the underlying output stream
* in little endian order, low byte first, high byte last
*
* @param pLong the {@code long} to be written.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeLong(long pLong) throws IOException {
out.write((int) pLong & 0xFF);
out.write((int) (pLong >>> 8) & 0xFF);
out.write((int) (pLong >>> 16) & 0xFF);
out.write((int) (pLong >>> 24) & 0xFF);
out.write((int) (pLong >>> 32) & 0xFF);
out.write((int) (pLong >>> 40) & 0xFF);
out.write((int) (pLong >>> 48) & 0xFF);
out.write((int) (pLong >>> 56) & 0xFF);
bytesWritten += 8;
}
/**
* Writes a 4 byte Java float to the underlying output stream in
* little endian order.
*
* @param f the {@code float} value to be written.
* @throws IOException if an I/O error occurs.
*/
public final void writeFloat(float f) throws IOException {
writeInt(Float.floatToIntBits(f));
}
/**
* Writes an 8 byte Java double to the underlying output stream in
* little endian order.
*
* @param d the {@code double} value to be written.
* @throws IOException if an I/O error occurs.
*/
public final void writeDouble(double d) throws IOException {
writeLong(Double.doubleToLongBits(d));
}
/**
* Writes a string to the underlying output stream as a sequence of
* bytes. Each character is written to the data output stream as
* if by the {@link #writeByte(int)} method.
*
* @param pString the {@code String} value to be written.
* @throws IOException if the underlying stream throws an IOException.
* @see #writeByte(int)
* @see #out
*/
public void writeBytes(String pString) throws IOException {
int length = pString.length();
for (int i = 0; i < length; i++) {
out.write((byte) pString.charAt(i));
}
bytesWritten += length;
}
/**
* Writes a string to the underlying output stream as a sequence of
* characters. Each character is written to the data output stream as
* if by the {@code writeChar} method.
*
* @param pString a {@code String} value to be written.
* @throws IOException if the underlying stream throws an IOException.
* @see #writeChar(int)
* @see #out
*/
public void writeChars(String pString) throws IOException {
int length = pString.length();
for (int i = 0; i < length; i++) {
int c = pString.charAt(i);
out.write(c & 0xFF);
out.write((c >>> 8) & 0xFF);
}
bytesWritten += length * 2;
}
/**
* Writes a string of no more than 65,535 characters
* to the underlying output stream using UTF-8
* encoding. This method first writes a two byte short
* in <b>big</b> endian order as required by the
* UTF-8 specification. This gives the number of bytes in the
* UTF-8 encoded version of the string, not the number of characters
* in the string. Next each character of the string is written
* using the UTF-8 encoding for the character.
*
* @param pString the string to be written.
* @throws UTFDataFormatException if the string is longer than
* 65,535 characters.
* @throws IOException if the underlying stream throws an IOException.
*/
public void writeUTF(String pString) throws IOException {
int numchars = pString.length();
int numbytes = 0;
for (int i = 0; i < numchars; i++) {
int c = pString.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
numbytes++;
}
else if (c > 0x07FF) {
numbytes += 3;
}
else {
numbytes += 2;
}
}
if (numbytes > 65535) {
throw new UTFDataFormatException();
}
out.write((numbytes >>> 8) & 0xFF);
out.write(numbytes & 0xFF);
for (int i = 0; i < numchars; i++) {
int c = pString.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
out.write(c);
}
else if (c > 0x07FF) {
out.write(0xE0 | ((c >> 12) & 0x0F));
out.write(0x80 | ((c >> 6) & 0x3F));
out.write(0x80 | (c & 0x3F));
bytesWritten += 2;
}
else {
out.write(0xC0 | ((c >> 6) & 0x1F));
out.write(0x80 | (c & 0x3F));
bytesWritten += 1;
}
}
bytesWritten += numchars + 2;
}
/**
* Returns the number of bytes written to this little endian output stream.
* (This class is not thread-safe with respect to this method. It is
* possible that this number is temporarily less than the actual
* number of bytes written.)
* @return the value of the {@code written} field.
* @see #bytesWritten
*/
public int size() {
return bytesWritten;
}
}

View File

@@ -1,198 +1,197 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* A {@code SeekableInputStream} implementation that caches data in memory.
* <p/>
*
* @see FileCacheSeekableStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/MemoryCacheSeekableStream.java#3 $
*/
public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStream {
/**
* Creates a {@code MemoryCacheSeekableStream}, reading from the given
* {@code InputStream}. Data will be cached in memory.
*
* @param pStream the {@code InputStream} to read from.
*/
public MemoryCacheSeekableStream(final InputStream pStream) {
super(pStream, new MemoryCache());
}
public final boolean isCachedMemory() {
return true;
}
public final boolean isCachedFile() {
return false;
}
final static class MemoryCache extends StreamCache {
final static int BLOCK_SIZE = 1 << 13;
private final List<byte[]> cache = new ArrayList<>();
private long length;
private long position;
private long start;
private byte[] getBlock() throws IOException {
final long currPos = position - start;
if (currPos < 0) {
throw new IOException("StreamCache flushed before read position");
}
long index = currPos / BLOCK_SIZE;
if (index >= Integer.MAX_VALUE) {
throw new IOException("Memory cache max size exceeded");
}
if (index >= cache.size()) {
try {
cache.add(new byte[BLOCK_SIZE]);
// System.out.println("Allocating new block, size: " + BLOCK_SIZE);
// System.out.println("New total size: " + cache.size() * BLOCK_SIZE + " (" + cache.size() + " blocks)");
}
catch (OutOfMemoryError e) {
throw new IOException("No more memory for cache: " + cache.size() * BLOCK_SIZE);
}
}
//System.out.println("index: " + index);
return cache.get((int) index);
}
public void write(final int pByte) throws IOException {
byte[] buffer = getBlock();
int idx = (int) (position % BLOCK_SIZE);
buffer[idx] = (byte) pByte;
position++;
if (position > length) {
length = position;
}
}
// TODO: OptimizeMe!!!
@Override
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
byte[] buffer = getBlock();
for (int i = 0; i < pLength; i++) {
int index = (int) position % BLOCK_SIZE;
if (index == 0) {
buffer = getBlock();
}
buffer[index] = pBuffer[pOffset + i];
position++;
}
if (position > length) {
length = position;
}
}
public int read() throws IOException {
if (position >= length) {
return -1;
}
byte[] buffer = getBlock();
int idx = (int) (position % BLOCK_SIZE);
position++;
return buffer[idx] & 0xff;
}
// TODO: OptimizeMe!!!
@Override
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (position >= length) {
return -1;
}
byte[] buffer = getBlock();
int bufferPos = (int) (position % BLOCK_SIZE);
// Find maxIdx and simplify test in for-loop
int maxLen = (int) Math.min(Math.min(pLength, buffer.length - bufferPos), length - position);
int i;
//for (i = 0; i < pLength && i < buffer.length - idx && i < length - position; i++) {
for (i = 0; i < maxLen; i++) {
pBytes[pOffset + i] = buffer[bufferPos + i];
}
position += i;
return i;
}
public void seek(final long pPosition) throws IOException {
if (pPosition < start) {
throw new IOException("Seek before flush position");
}
position = pPosition;
}
@Override
public void flush(final long pPosition) {
int firstPos = (int) (pPosition / BLOCK_SIZE) - 1;
for (int i = 0; i < firstPos; i++) {
cache.remove(0);
}
start = pPosition;
}
@Override
void close() throws IOException {
cache.clear();
}
public long getPosition() {
return position;
}
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* A {@code SeekableInputStream} implementation that caches data in memory.
*
* @see FileCacheSeekableStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/MemoryCacheSeekableStream.java#3 $
*/
public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStream {
/**
* Creates a {@code MemoryCacheSeekableStream}, reading from the given
* {@code InputStream}. Data will be cached in memory.
*
* @param pStream the {@code InputStream} to read from.
*/
public MemoryCacheSeekableStream(final InputStream pStream) {
super(pStream, new MemoryCache());
}
public final boolean isCachedMemory() {
return true;
}
public final boolean isCachedFile() {
return false;
}
final static class MemoryCache extends StreamCache {
final static int BLOCK_SIZE = 1 << 13;
private final List<byte[]> cache = new ArrayList<>();
private long length;
private long position;
private long start;
private byte[] getBlock() throws IOException {
final long currPos = position - start;
if (currPos < 0) {
throw new IOException("StreamCache flushed before read position");
}
long index = currPos / BLOCK_SIZE;
if (index >= Integer.MAX_VALUE) {
throw new IOException("Memory cache max size exceeded");
}
if (index >= cache.size()) {
try {
cache.add(new byte[BLOCK_SIZE]);
// System.out.println("Allocating new block, size: " + BLOCK_SIZE);
// System.out.println("New total size: " + cache.size() * BLOCK_SIZE + " (" + cache.size() + " blocks)");
}
catch (OutOfMemoryError e) {
throw new IOException("No more memory for cache: " + cache.size() * BLOCK_SIZE);
}
}
//System.out.println("index: " + index);
return cache.get((int) index);
}
public void write(final int pByte) throws IOException {
byte[] buffer = getBlock();
int idx = (int) (position % BLOCK_SIZE);
buffer[idx] = (byte) pByte;
position++;
if (position > length) {
length = position;
}
}
// TODO: OptimizeMe!!!
@Override
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
byte[] buffer = getBlock();
for (int i = 0; i < pLength; i++) {
int index = (int) position % BLOCK_SIZE;
if (index == 0) {
buffer = getBlock();
}
buffer[index] = pBuffer[pOffset + i];
position++;
}
if (position > length) {
length = position;
}
}
public int read() throws IOException {
if (position >= length) {
return -1;
}
byte[] buffer = getBlock();
int idx = (int) (position % BLOCK_SIZE);
position++;
return buffer[idx] & 0xff;
}
// TODO: OptimizeMe!!!
@Override
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (position >= length) {
return -1;
}
byte[] buffer = getBlock();
int bufferPos = (int) (position % BLOCK_SIZE);
// Find maxIdx and simplify test in for-loop
int maxLen = (int) Math.min(Math.min(pLength, buffer.length - bufferPos), length - position);
int i;
//for (i = 0; i < pLength && i < buffer.length - idx && i < length - position; i++) {
for (i = 0; i < maxLen; i++) {
pBytes[pOffset + i] = buffer[bufferPos + i];
}
position += i;
return i;
}
public void seek(final long pPosition) throws IOException {
if (pPosition < start) {
throw new IOException("Seek before flush position");
}
position = pPosition;
}
@Override
public void flush(final long pPosition) {
int firstPos = (int) (pPosition / BLOCK_SIZE) - 1;
for (int i = 0; i < firstPos; i++) {
cache.remove(0);
}
start = pPosition;
}
@Override
void close() throws IOException {
cache.clear();
}
public long getPosition() {
return position;
}
}
}

View File

@@ -1,82 +1,81 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.IOException;
import java.io.InputStream;
/**
* An {@code InputStream} that contains no bytes.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/NullInputStream.java#2 $
*/
public class NullInputStream extends InputStream {
/**
* Creates a {@code NullInputStream}.
*/
public NullInputStream() {
}
/**
* This implementation returns {@code -1} (EOF), always.
*
* @return {@code -1}
* @throws IOException
*/
public int read() throws IOException {
return -1;
}
/**
* This implementation returns {@code 0}, always.
*
* @return {@code 0}
* @throws IOException
*/
@Override
public int available() throws IOException {
return 0;
}
/**
* This implementation returns {@code 0}, always.
*
* @return {@code 0}
* @throws IOException
*/
@Override
public long skip(long pOffset) throws IOException {
return 0l;
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.IOException;
import java.io.InputStream;
/**
* An {@code InputStream} that contains no bytes.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/NullInputStream.java#2 $
*/
public class NullInputStream extends InputStream {
/**
* Creates a {@code NullInputStream}.
*/
public NullInputStream() {
}
/**
* This implementation returns {@code -1} (EOF), always.
*
* @return {@code -1}
* @throws IOException
*/
public int read() throws IOException {
return -1;
}
/**
* This implementation returns {@code 0}, always.
*
* @return {@code 0}
* @throws IOException
*/
@Override
public int available() throws IOException {
return 0;
}
/**
* This implementation returns {@code 0}, always.
*
* @return {@code 0}
* @throws IOException
*/
@Override
public long skip(long pOffset) throws IOException {
return 0l;
}
}

View File

@@ -1,70 +1,69 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.IOException;
import java.io.OutputStream;
/**
* An {@code OutputStream} implementation that works as a sink.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/NullOutputStream.java#2 $
*/
public class NullOutputStream extends OutputStream {
/**
* Creates a {@code NullOutputStream}.
*/
public NullOutputStream() {
}
/**
* This implementation does nothing.
*/
public void write(int pByte) throws IOException {
}
/**
* This implementation does nothing.
*/
@Override
public void write(byte pBytes[]) throws IOException {
}
/**
* This implementation does nothing.
*/
@Override
public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.IOException;
import java.io.OutputStream;
/**
* An {@code OutputStream} implementation that works as a sink.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/NullOutputStream.java#2 $
*/
public class NullOutputStream extends OutputStream {
/**
* Creates a {@code NullOutputStream}.
*/
public NullOutputStream() {
}
/**
* This implementation does nothing.
*/
public void write(int pByte) throws IOException {
}
/**
* This implementation does nothing.
*/
@Override
public void write(byte pBytes[]) throws IOException {
}
/**
* This implementation does nothing.
*/
@Override
public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
}
}

View File

@@ -1,241 +1,242 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.EOFException;
import java.io.IOException;
/**
* A data stream that is both readable and writable, much like a
* {@code RandomAccessFile}, except it may be backed by something other than a file.
* <p/>
*
* @see java.io.RandomAccessFile
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/RandomAccessStream.java#3 $
*/
public abstract class RandomAccessStream implements Seekable, DataInput, DataOutput {
// TODO: Use a RandomAcceessFile as backing in impl, probably
// TODO: Create an in-memory implementation too?
// TODO: Package private SeekableDelegate?
// TODO: Both read and write must update stream position
//private int position = -1;
/** This random access stream, wrapped in an {@code InputStream} */
SeekableInputStream inputView = null;
/** This random access stream, wrapped in an {@code OutputStream} */
SeekableOutputStream outputView = null;
// TODO: Create an Input and an Output interface matching InputStream and OutputStream?
public int read() throws IOException {
try {
return readByte() & 0xff;
}
catch (EOFException e) {
return -1;
}
}
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (pBytes == null) {
throw new NullPointerException("bytes == null");
}
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
throw new IndexOutOfBoundsException();
}
else if (pLength == 0) {
return 0;
}
// Special case, allready at EOF
int c = read();
if (c == -1) {
return -1;
}
// Otherwise, read as many as bytes as possible
pBytes[pOffset] = (byte) c;
int i = 1;
try {
for (; i < pLength; i++) {
c = read();
if (c == -1) {
break;
}
pBytes[pOffset + i] = (byte) c;
}
}
catch (IOException ignore) {
// Ignore exception, just return length
}
return i;
}
public final int read(byte[] pBytes) throws IOException {
return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
}
/**
* Returns an input view of this {@code RandomAccessStream}.
* Invoking this method several times, will return the same object.
* <p/>
* <em>Note that read access is NOT synchronized.</em>
*
* @return a {@code SeekableInputStream} reading from this stream
*/
public final SeekableInputStream asInputStream() {
if (inputView == null) {
inputView = new InputStreamView(this);
}
return inputView;
}
/**
* Returns an output view of this {@code RandomAccessStream}.
* Invoking this method several times, will return the same object.
* <p/>
* <em>Note that write access is NOT synchronized.</em>
*
* @return a {@code SeekableOutputStream} writing to this stream
*/
public final SeekableOutputStream asOutputStream() {
if (outputView == null) {
outputView = new OutputStreamView(this);
}
return outputView;
}
static final class InputStreamView extends SeekableInputStream {
// TODO: Consider adding synchonization (on stream) for all operations
// TODO: Is is a good thing that close/flush etc works on stream?
// - Or should it rather just work on the views?
// - Allow multiple views?
final private RandomAccessStream mStream;
public InputStreamView(RandomAccessStream pStream) {
if (pStream == null) {
throw new IllegalArgumentException("stream == null");
}
mStream = pStream;
}
public boolean isCached() {
return mStream.isCached();
}
public boolean isCachedFile() {
return mStream.isCachedFile();
}
public boolean isCachedMemory() {
return mStream.isCachedMemory();
}
protected void closeImpl() throws IOException {
mStream.close();
}
protected void flushBeforeImpl(long pPosition) throws IOException {
mStream.flushBefore(pPosition);
}
protected void seekImpl(long pPosition) throws IOException {
mStream.seek(pPosition);
}
public int read() throws IOException {
return mStream.read();
}
@Override
public int read(byte pBytes[], int pOffset, int pLength) throws IOException {
return mStream.read(pBytes, pOffset, pLength);
}
}
static final class OutputStreamView extends SeekableOutputStream {
// TODO: Consider adding synchonization (on stream) for all operations
// TODO: Is is a good thing that close/flush etc works on stream?
// - Or should it rather just work on the views?
// - Allow multiple views?
final private RandomAccessStream mStream;
public OutputStreamView(RandomAccessStream pStream) {
if (pStream == null) {
throw new IllegalArgumentException("stream == null");
}
mStream = pStream;
}
public boolean isCached() {
return mStream.isCached();
}
public boolean isCachedFile() {
return mStream.isCachedFile();
}
public boolean isCachedMemory() {
return mStream.isCachedMemory();
}
protected void closeImpl() throws IOException {
mStream.close();
}
protected void flushBeforeImpl(long pPosition) throws IOException {
mStream.flushBefore(pPosition);
}
protected void seekImpl(long pPosition) throws IOException {
mStream.seek(pPosition);
}
public void write(int pByte) throws IOException {
mStream.write(pByte);
}
@Override
public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
mStream.write(pBytes, pOffset, pLength);
}
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.EOFException;
import java.io.IOException;
/**
* A data stream that is both readable and writable, much like a
* {@code RandomAccessFile}, except it may be backed by something other than a file.
*
* @see java.io.RandomAccessFile
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/RandomAccessStream.java#3 $
*/
public abstract class RandomAccessStream implements Seekable, DataInput, DataOutput {
// TODO: Use a RandomAcceessFile as backing in impl, probably
// TODO: Create an in-memory implementation too?
// TODO: Package private SeekableDelegate?
// TODO: Both read and write must update stream position
//private int position = -1;
/** This random access stream, wrapped in an {@code InputStream} */
SeekableInputStream inputView = null;
/** This random access stream, wrapped in an {@code OutputStream} */
SeekableOutputStream outputView = null;
// TODO: Create an Input and an Output interface matching InputStream and OutputStream?
public int read() throws IOException {
try {
return readByte() & 0xff;
}
catch (EOFException e) {
return -1;
}
}
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (pBytes == null) {
throw new NullPointerException("bytes == null");
}
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
throw new IndexOutOfBoundsException();
}
else if (pLength == 0) {
return 0;
}
// Special case, allready at EOF
int c = read();
if (c == -1) {
return -1;
}
// Otherwise, read as many as bytes as possible
pBytes[pOffset] = (byte) c;
int i = 1;
try {
for (; i < pLength; i++) {
c = read();
if (c == -1) {
break;
}
pBytes[pOffset + i] = (byte) c;
}
}
catch (IOException ignore) {
// Ignore exception, just return length
}
return i;
}
public final int read(byte[] pBytes) throws IOException {
return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
}
/**
* Returns an input view of this {@code RandomAccessStream}.
* Invoking this method several times, will return the same object.
* <p>
* <em>Note that read access is NOT synchronized.</em>
* </p>
*
* @return a {@code SeekableInputStream} reading from this stream
*/
public final SeekableInputStream asInputStream() {
if (inputView == null) {
inputView = new InputStreamView(this);
}
return inputView;
}
/**
* Returns an output view of this {@code RandomAccessStream}.
* Invoking this method several times, will return the same object.
* <p>
* <em>Note that write access is NOT synchronized.</em>
* </p>
*
* @return a {@code SeekableOutputStream} writing to this stream
*/
public final SeekableOutputStream asOutputStream() {
if (outputView == null) {
outputView = new OutputStreamView(this);
}
return outputView;
}
static final class InputStreamView extends SeekableInputStream {
// TODO: Consider adding synchonization (on stream) for all operations
// TODO: Is is a good thing that close/flush etc works on stream?
// - Or should it rather just work on the views?
// - Allow multiple views?
final private RandomAccessStream mStream;
public InputStreamView(RandomAccessStream pStream) {
if (pStream == null) {
throw new IllegalArgumentException("stream == null");
}
mStream = pStream;
}
public boolean isCached() {
return mStream.isCached();
}
public boolean isCachedFile() {
return mStream.isCachedFile();
}
public boolean isCachedMemory() {
return mStream.isCachedMemory();
}
protected void closeImpl() throws IOException {
mStream.close();
}
protected void flushBeforeImpl(long pPosition) throws IOException {
mStream.flushBefore(pPosition);
}
protected void seekImpl(long pPosition) throws IOException {
mStream.seek(pPosition);
}
public int read() throws IOException {
return mStream.read();
}
@Override
public int read(byte pBytes[], int pOffset, int pLength) throws IOException {
return mStream.read(pBytes, pOffset, pLength);
}
}
static final class OutputStreamView extends SeekableOutputStream {
// TODO: Consider adding synchonization (on stream) for all operations
// TODO: Is is a good thing that close/flush etc works on stream?
// - Or should it rather just work on the views?
// - Allow multiple views?
final private RandomAccessStream mStream;
public OutputStreamView(RandomAccessStream pStream) {
if (pStream == null) {
throw new IllegalArgumentException("stream == null");
}
mStream = pStream;
}
public boolean isCached() {
return mStream.isCached();
}
public boolean isCachedFile() {
return mStream.isCachedFile();
}
public boolean isCachedMemory() {
return mStream.isCachedMemory();
}
protected void closeImpl() throws IOException {
mStream.close();
}
protected void flushBeforeImpl(long pPosition) throws IOException {
mStream.flushBefore(pPosition);
}
protected void seekImpl(long pPosition) throws IOException {
mStream.seek(pPosition);
}
public void write(int pByte) throws IOException {
mStream.write(pByte);
}
@Override
public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
mStream.write(pBytes, pOffset, pLength);
}
}
}

View File

@@ -1,186 +1,193 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.IOException;
/**
* Interface for seekable streams.
* <p/>
* @see SeekableInputStream
* @see SeekableOutputStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Seekable.java#1 $
*/
public interface Seekable {
/**
* Returns the current byte position of the stream. The next read will take
* place starting at this offset.
*
* @return a {@code long} containing the position of the stream.
* @throws IOException if an I/O error occurs.
*/
long getStreamPosition() throws IOException;
/**
* Sets the current stream position to the desired location.
* The next read will occur at this location.
* <p/>
* An {@code IndexOutOfBoundsException} will be thrown if pPosition is smaller
* than the flushed position (as returned by {@link #getFlushedPosition()}).
* <p/>
* It is legal to seek past the end of the file; an {@code EOFException}
* will be thrown only if a read is performed.
*
* @param pPosition a long containing the desired file pointer position.
*
* @throws IndexOutOfBoundsException if {@code pPosition} is smaller than
* the flushed position.
* @throws IOException if any other I/O error occurs.
*/
void seek(long pPosition) throws IOException;
/**
* Marks a position in the stream to be returned to by a subsequent call to
* reset.
* Unlike a standard {@code InputStream}, all {@code Seekable}
* streams upport marking. Additionally, calls to {@code mark} and
* {@code reset} may be nested arbitrarily.
* <p/>
* Unlike the {@code mark} methods declared by the {@code Reader} or
* {@code InputStream}
* interfaces, no {@code readLimit} parameter is used. An arbitrary amount
* of data may be read following the call to {@code mark}.
*/
void mark();
/**
* Returns the file pointer to its previous position,
* at the time of the most recent unmatched call to mark.
* <p/>
* Calls to reset without a corresponding call to mark will either:
* <ul>
* <li>throw an {@code IOException}</li>
* <li>or, reset to the beginning of the stream.</li>
* </ul>
* An {@code IOException} will be thrown if the previous marked position
* lies in the discarded portion of the stream.
*
* @throws IOException if an I/O error occurs.
* @see java.io.InputStream#reset()
*/
void reset() throws IOException;
/**
* Discards the initial portion of the stream prior to the indicated
* postion. Attempting to seek to an offset within the flushed portion of
* the stream will result in an {@code IndexOutOfBoundsException}.
* <p/>
* Calling {@code flushBefore} may allow classes implementing this
* interface to free up resources such as memory or disk space that are
* being used to store data from the stream.
*
* @param pPosition a long containing the length of the file prefix that
* may be flushed.
*
* @throws IndexOutOfBoundsException if {@code pPosition} lies in the
* flushed portion of the stream or past the current stream position.
* @throws IOException if an I/O error occurs.
*/
void flushBefore(long pPosition) throws IOException;
/**
* Discards the initial position of the stream prior to the current stream
* position. Equivalent to {@code flushBefore(getStreamPosition())}.
*
* @throws IOException if an I/O error occurs.
*/
void flush() throws IOException;
/**
* Returns the earliest position in the stream to which seeking may be
* performed. The returned value will be the maximum of all values passed
* into previous calls to {@code flushBefore}.
*
* @return the earliest legal position for seeking, as a {@code long}.
*
* @throws IOException if an I/O error occurs.
*/
long getFlushedPosition() throws IOException;
/**
* Returns true if this {@code Seekable} stream caches data itself in order
* to allow seeking backwards. Applications may consult this in order to
* decide how frequently, or whether, to flush in order to conserve cache
* resources.
*
* @return {@code true} if this {@code Seekable} caches data.
* @see #isCachedMemory()
* @see #isCachedFile()
*/
boolean isCached();
/**
* Returns true if this {@code Seekable} stream caches data itself in order
* to allow seeking backwards, and the cache is kept in main memory.
* Applications may consult this in order to decide how frequently, or
* whether, to flush in order to conserve cache resources.
*
* @return {@code true} if this {@code Seekable} caches data in main
* memory.
* @see #isCached()
* @see #isCachedFile()
*/
boolean isCachedMemory();
/**
* Returns true if this {@code Seekable} stream caches data itself in
* order to allow seeking backwards, and the cache is kept in a
* temporary file.
* Applications may consult this in order to decide how frequently,
* or whether, to flush in order to conserve cache resources.
*
* @return {@code true} if this {@code Seekable} caches data in a
* temporary file.
* @see #isCached
* @see #isCachedMemory
*/
boolean isCachedFile();
/**
* Closes the stream.
*
* @throws java.io.IOException if the stream can't be closed.
*/
void close() throws IOException;
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.IOException;
/**
* Interface for seekable streams.
*
* @see SeekableInputStream
* @see SeekableOutputStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Seekable.java#1 $
*/
public interface Seekable {
/**
* Returns the current byte position of the stream. The next read will take
* place starting at this offset.
*
* @return a {@code long} containing the position of the stream.
* @throws IOException if an I/O error occurs.
*/
long getStreamPosition() throws IOException;
/**
* Sets the current stream position to the desired location.
* The next read will occur at this location.
* <p>
* An {@code IndexOutOfBoundsException} will be thrown if pPosition is smaller
* than the flushed position (as returned by {@link #getFlushedPosition()}).
* </p>
* <p>
* It is legal to seek past the end of the file; an {@code EOFException}
* will be thrown only if a read is performed.
* </p>
*
* @param pPosition a long containing the desired file pointer position.
*
* @throws IndexOutOfBoundsException if {@code pPosition} is smaller than
* the flushed position.
* @throws IOException if any other I/O error occurs.
*/
void seek(long pPosition) throws IOException;
/**
* Marks a position in the stream to be returned to by a subsequent call to
* reset.
* Unlike a standard {@code InputStream}, all {@code Seekable}
* streams upport marking. Additionally, calls to {@code mark} and
* {@code reset} may be nested arbitrarily.
* <p>
* Unlike the {@code mark} methods declared by the {@code Reader} or
* {@code InputStream}
* interfaces, no {@code readLimit} parameter is used. An arbitrary amount
* of data may be read following the call to {@code mark}.
* </p>
*/
void mark();
/**
* Returns the file pointer to its previous position,
* at the time of the most recent unmatched call to mark.
* <p>
* Calls to reset without a corresponding call to mark will either:
* </p>
* <ul>
* <li>throw an {@code IOException}</li>
* <li>or, reset to the beginning of the stream.</li>
* </ul>
* <p>
* An {@code IOException} will be thrown if the previous marked position
* lies in the discarded portion of the stream.
* </p>
*
* @throws IOException if an I/O error occurs.
* @see java.io.InputStream#reset()
*/
void reset() throws IOException;
/**
* Discards the initial portion of the stream prior to the indicated
* postion. Attempting to seek to an offset within the flushed portion of
* the stream will result in an {@code IndexOutOfBoundsException}.
* <p>
* Calling {@code flushBefore} may allow classes implementing this
* interface to free up resources such as memory or disk space that are
* being used to store data from the stream.
* </p>
*
* @param pPosition a long containing the length of the file prefix that
* may be flushed.
*
* @throws IndexOutOfBoundsException if {@code pPosition} lies in the
* flushed portion of the stream or past the current stream position.
* @throws IOException if an I/O error occurs.
*/
void flushBefore(long pPosition) throws IOException;
/**
* Discards the initial position of the stream prior to the current stream
* position. Equivalent to {@code flushBefore(getStreamPosition())}.
*
* @throws IOException if an I/O error occurs.
*/
void flush() throws IOException;
/**
* Returns the earliest position in the stream to which seeking may be
* performed. The returned value will be the maximum of all values passed
* into previous calls to {@code flushBefore}.
*
* @return the earliest legal position for seeking, as a {@code long}.
*
* @throws IOException if an I/O error occurs.
*/
long getFlushedPosition() throws IOException;
/**
* Returns true if this {@code Seekable} stream caches data itself in order
* to allow seeking backwards. Applications may consult this in order to
* decide how frequently, or whether, to flush in order to conserve cache
* resources.
*
* @return {@code true} if this {@code Seekable} caches data.
* @see #isCachedMemory()
* @see #isCachedFile()
*/
boolean isCached();
/**
* Returns true if this {@code Seekable} stream caches data itself in order
* to allow seeking backwards, and the cache is kept in main memory.
* Applications may consult this in order to decide how frequently, or
* whether, to flush in order to conserve cache resources.
*
* @return {@code true} if this {@code Seekable} caches data in main
* memory.
* @see #isCached()
* @see #isCachedFile()
*/
boolean isCachedMemory();
/**
* Returns true if this {@code Seekable} stream caches data itself in
* order to allow seeking backwards, and the cache is kept in a
* temporary file.
* Applications may consult this in order to decide how frequently,
* or whether, to flush in order to conserve cache resources.
*
* @return {@code true} if this {@code Seekable} caches data in a
* temporary file.
* @see #isCached
* @see #isCachedMemory
*/
boolean isCachedFile();
/**
* Closes the stream.
*
* @throws java.io.IOException if the stream can't be closed.
*/
void close() throws IOException;
}

View File

@@ -1,238 +1,238 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;
/**
* Abstract base class for {@code InputStream}s implementing the {@code Seekable} interface.
* <p/>
* @see SeekableOutputStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SeekableInputStream.java#4 $
*/
public abstract class SeekableInputStream extends InputStream implements Seekable {
// TODO: It's at the moment not possible to create subclasses outside this
// package, as there's no access to position. position needs to be
// updated from the read/read/read methods...
/** The stream position in this stream */
long position;
long flushedPosition;
boolean closed;
protected Stack<Long> markedPositions = new Stack<Long>();
/// InputStream overrides
@Override
public final int read(byte[] pBytes) throws IOException {
return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
}
/**
* Implemented using {@code seek(currentPos + pLength)}.
*
* @param pLength the number of bytes to skip
* @return the actual number of bytes skipped (may be equal to or less
* than {@code pLength})
*
* @throws IOException if an I/O exception occurs during skip
*/
@Override
public final long skip(final long pLength) throws IOException {
long pos = position;
long wantedPosition = pos + pLength;
if (wantedPosition < flushedPosition) {
throw new IOException("position < flushedPosition");
}
// Stop at stream length for compatibility, even though it might be allowed
// to seek past end of stream
int available = available();
if (available > 0) {
seek(Math.min(wantedPosition, pos + available));
}
// TODO: Add optimization for streams with known length!
else {
// Slow mode...
int toSkip = (int) Math.max(Math.min(pLength, 512), -512);
while (toSkip > 0 && read() >= 0) {
toSkip--;
}
}
return position - pos;
}
@Override
public final void mark(int pLimit) {
mark();
// TODO: We don't really need to do this.. Is it a good idea?
try {
flushBefore(Math.max(position - pLimit, flushedPosition));
}
catch (IOException ignore) {
// Ignore, as it's not really critical
}
}
/**
* Returns {@code true}, as marking is always supported.
*
* @return {@code true}.
*/
@Override
public final boolean markSupported() {
return true;
}
/// Seekable implementation
public final void seek(long pPosition) throws IOException {
checkOpen();
// NOTE: This is correct according to javax.imageio (IndexOutOfBoundsException),
// but it's kind of inconsistent with reset that throws IOException...
if (pPosition < flushedPosition) {
throw new IndexOutOfBoundsException("position < flushedPosition");
}
seekImpl(pPosition);
position = pPosition;
}
protected abstract void seekImpl(long pPosition) throws IOException;
public final void mark() {
markedPositions.push(position);
}
@Override
public final void reset() throws IOException {
checkOpen();
if (!markedPositions.isEmpty()) {
long newPos = markedPositions.pop();
// NOTE: This is correct according to javax.imageio (IOException),
// but it's kind of inconsistent with seek that throws IndexOutOfBoundsException...
if (newPos < flushedPosition) {
throw new IOException("Previous marked position has been discarded");
}
seek(newPos);
}
else {
// TODO: To iron out some wrinkles due to conflicting contracts
// (InputStream and Seekable both declare reset),
// we might need to reset to the last marked position instead..
// However, that becomes REALLY confusing if that position is after
// the current position...
seek(0);
}
}
public final void flushBefore(long pPosition) throws IOException {
if (pPosition < flushedPosition) {
throw new IndexOutOfBoundsException("position < flushedPosition");
}
if (pPosition > getStreamPosition()) {
throw new IndexOutOfBoundsException("position > stream position");
}
checkOpen();
flushBeforeImpl(pPosition);
flushedPosition = pPosition;
}
/**
* Discards the initial portion of the stream prior to the indicated postion.
*
* @param pPosition the position to flush to
* @throws IOException if an I/O exception occurs during the flush operation
*
* @see #flushBefore(long)
*/
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
public final void flush() throws IOException {
flushBefore(flushedPosition);
}
public final long getFlushedPosition() throws IOException {
checkOpen();
return flushedPosition;
}
public final long getStreamPosition() throws IOException {
checkOpen();
return position;
}
protected final void checkOpen() throws IOException {
if (closed) {
throw new IOException("closed");
}
}
@Override
public final void close() throws IOException {
checkOpen();
closed = true;
closeImpl();
}
protected abstract void closeImpl() throws IOException;
/**
* Finalizes this object prior to garbage collection. The
* {@code close} method is called to close any open input
* source. This method should not be called from application
* code.
*
* @exception Throwable if an error occurs during superclass
* finalization.
*/
@Override
protected void finalize() throws Throwable {
if (!closed) {
try {
close();
}
catch (IOException ignore) {
// Ignroe
}
}
super.finalize();
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;
/**
* Abstract base class for {@code InputStream}s implementing the {@code Seekable} interface.
*
* @see SeekableOutputStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SeekableInputStream.java#4 $
*/
public abstract class SeekableInputStream extends InputStream implements Seekable {
// TODO: It's at the moment not possible to create subclasses outside this
// package, as there's no access to position. position needs to be
// updated from the read/read/read methods...
/** The stream position in this stream */
long position;
long flushedPosition;
boolean closed;
protected Stack<Long> markedPositions = new Stack<Long>();
/// InputStream overrides
@Override
public final int read(byte[] pBytes) throws IOException {
return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
}
/**
* Implemented using {@code seek(currentPos + pLength)}.
*
* @param pLength the number of bytes to skip
* @return the actual number of bytes skipped (may be equal to or less
* than {@code pLength})
*
* @throws IOException if an I/O exception occurs during skip
*/
@Override
public final long skip(final long pLength) throws IOException {
long pos = position;
long wantedPosition = pos + pLength;
if (wantedPosition < flushedPosition) {
throw new IOException("position < flushedPosition");
}
// Stop at stream length for compatibility, even though it might be allowed
// to seek past end of stream
int available = available();
if (available > 0) {
seek(Math.min(wantedPosition, pos + available));
}
// TODO: Add optimization for streams with known length!
else {
// Slow mode...
int toSkip = (int) Math.max(Math.min(pLength, 512), -512);
while (toSkip > 0 && read() >= 0) {
toSkip--;
}
}
return position - pos;
}
@Override
public final void mark(int pLimit) {
mark();
// TODO: We don't really need to do this.. Is it a good idea?
try {
flushBefore(Math.max(position - pLimit, flushedPosition));
}
catch (IOException ignore) {
// Ignore, as it's not really critical
}
}
/**
* Returns {@code true}, as marking is always supported.
*
* @return {@code true}.
*/
@Override
public final boolean markSupported() {
return true;
}
/// Seekable implementation
public final void seek(long pPosition) throws IOException {
checkOpen();
// NOTE: This is correct according to javax.imageio (IndexOutOfBoundsException),
// but it's kind of inconsistent with reset that throws IOException...
if (pPosition < flushedPosition) {
throw new IndexOutOfBoundsException("position < flushedPosition");
}
seekImpl(pPosition);
position = pPosition;
}
protected abstract void seekImpl(long pPosition) throws IOException;
public final void mark() {
markedPositions.push(position);
}
@Override
public final void reset() throws IOException {
checkOpen();
if (!markedPositions.isEmpty()) {
long newPos = markedPositions.pop();
// NOTE: This is correct according to javax.imageio (IOException),
// but it's kind of inconsistent with seek that throws IndexOutOfBoundsException...
if (newPos < flushedPosition) {
throw new IOException("Previous marked position has been discarded");
}
seek(newPos);
}
else {
// TODO: To iron out some wrinkles due to conflicting contracts
// (InputStream and Seekable both declare reset),
// we might need to reset to the last marked position instead..
// However, that becomes REALLY confusing if that position is after
// the current position...
seek(0);
}
}
public final void flushBefore(long pPosition) throws IOException {
if (pPosition < flushedPosition) {
throw new IndexOutOfBoundsException("position < flushedPosition");
}
if (pPosition > getStreamPosition()) {
throw new IndexOutOfBoundsException("position > stream position");
}
checkOpen();
flushBeforeImpl(pPosition);
flushedPosition = pPosition;
}
/**
* Discards the initial portion of the stream prior to the indicated postion.
*
* @param pPosition the position to flush to
* @throws IOException if an I/O exception occurs during the flush operation
*
* @see #flushBefore(long)
*/
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
public final void flush() throws IOException {
flushBefore(flushedPosition);
}
public final long getFlushedPosition() throws IOException {
checkOpen();
return flushedPosition;
}
public final long getStreamPosition() throws IOException {
checkOpen();
return position;
}
protected final void checkOpen() throws IOException {
if (closed) {
throw new IOException("closed");
}
}
@Override
public final void close() throws IOException {
checkOpen();
closed = true;
closeImpl();
}
protected abstract void closeImpl() throws IOException;
/**
* Finalizes this object prior to garbage collection. The
* {@code close} method is called to close any open input
* source. This method should not be called from application
* code.
*
* @exception Throwable if an error occurs during superclass
* finalization.
*/
@Override
protected void finalize() throws Throwable {
if (!closed) {
try {
close();
}
catch (IOException ignore) {
// Ignroe
}
}
super.finalize();
}
}

View File

@@ -1,140 +1,140 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Stack;
/**
* Abstract base class for {@code OutputStream}s implementing the
* {@code Seekable} interface.
* <p/>
* @see SeekableInputStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SeekableOutputStream.java#2 $
*/
public abstract class SeekableOutputStream extends OutputStream implements Seekable {
// TODO: Implement
long position;
long flushedPosition;
boolean closed;
protected Stack<Long> markedPositions = new Stack<Long>();
/// Outputstream overrides
@Override
public final void write(byte pBytes[]) throws IOException {
write(pBytes, 0, pBytes != null ? pBytes.length : 1);
}
/// Seekable implementation
// TODO: This is common behaviour/implementation with SeekableInputStream,
// probably a good idea to extract a delegate..?
public final void seek(long pPosition) throws IOException {
checkOpen();
// TODO: This is correct according to javax.imageio (IndexOutOfBoundsException),
// but it's inconsistent with reset that throws IOException...
if (pPosition < flushedPosition) {
throw new IndexOutOfBoundsException("position < flushedPosition!");
}
seekImpl(pPosition);
position = pPosition;
}
protected abstract void seekImpl(long pPosition) throws IOException;
public final void mark() {
markedPositions.push(position);
}
public final void reset() throws IOException {
checkOpen();
if (!markedPositions.isEmpty()) {
long newPos = markedPositions.pop();
// TODO: This is correct according to javax.imageio (IOException),
// but it's inconsistent with seek that throws IndexOutOfBoundsException...
if (newPos < flushedPosition) {
throw new IOException("Previous marked position has been discarded!");
}
seek(newPos);
}
}
public final void flushBefore(long pPosition) throws IOException {
if (pPosition < flushedPosition) {
throw new IndexOutOfBoundsException("position < flushedPosition!");
}
if (pPosition > getStreamPosition()) {
throw new IndexOutOfBoundsException("position > getStreamPosition()!");
}
checkOpen();
flushBeforeImpl(pPosition);
flushedPosition = pPosition;
}
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
@Override
public final void flush() throws IOException {
flushBefore(flushedPosition);
}
public final long getFlushedPosition() throws IOException {
checkOpen();
return flushedPosition;
}
public final long getStreamPosition() throws IOException {
checkOpen();
return position;
}
protected final void checkOpen() throws IOException {
if (closed) {
throw new IOException("closed");
}
}
@Override
public final void close() throws IOException {
checkOpen();
closed = true;
closeImpl();
}
protected abstract void closeImpl() throws IOException;
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Stack;
/**
* Abstract base class for {@code OutputStream}s implementing the
* {@code Seekable} interface.
*
* @see SeekableInputStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SeekableOutputStream.java#2 $
*/
public abstract class SeekableOutputStream extends OutputStream implements Seekable {
// TODO: Implement
long position;
long flushedPosition;
boolean closed;
protected Stack<Long> markedPositions = new Stack<Long>();
/// Outputstream overrides
@Override
public final void write(byte pBytes[]) throws IOException {
write(pBytes, 0, pBytes != null ? pBytes.length : 1);
}
/// Seekable implementation
// TODO: This is common behaviour/implementation with SeekableInputStream,
// probably a good idea to extract a delegate..?
public final void seek(long pPosition) throws IOException {
checkOpen();
// TODO: This is correct according to javax.imageio (IndexOutOfBoundsException),
// but it's inconsistent with reset that throws IOException...
if (pPosition < flushedPosition) {
throw new IndexOutOfBoundsException("position < flushedPosition!");
}
seekImpl(pPosition);
position = pPosition;
}
protected abstract void seekImpl(long pPosition) throws IOException;
public final void mark() {
markedPositions.push(position);
}
public final void reset() throws IOException {
checkOpen();
if (!markedPositions.isEmpty()) {
long newPos = markedPositions.pop();
// TODO: This is correct according to javax.imageio (IOException),
// but it's inconsistent with seek that throws IndexOutOfBoundsException...
if (newPos < flushedPosition) {
throw new IOException("Previous marked position has been discarded!");
}
seek(newPos);
}
}
public final void flushBefore(long pPosition) throws IOException {
if (pPosition < flushedPosition) {
throw new IndexOutOfBoundsException("position < flushedPosition!");
}
if (pPosition > getStreamPosition()) {
throw new IndexOutOfBoundsException("position > getStreamPosition()!");
}
checkOpen();
flushBeforeImpl(pPosition);
flushedPosition = pPosition;
}
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
@Override
public final void flush() throws IOException {
flushBefore(flushedPosition);
}
public final long getFlushedPosition() throws IOException {
checkOpen();
return flushedPosition;
}
public final long getStreamPosition() throws IOException {
checkOpen();
return position;
}
protected final void checkOpen() throws IOException {
if (closed) {
throw new IOException("closed");
}
}
@Override
public final void close() throws IOException {
checkOpen();
closed = true;
closeImpl();
}
protected abstract void closeImpl() throws IOException;
}

View File

@@ -1,189 +1,188 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
/**
* StringArrayReader
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/StringArrayReader.java#2 $
*/
public class StringArrayReader extends StringReader {
private StringReader current;
private String[] strings;
protected final Object finalLock;
private int currentSting;
private int markedString;
private int mark;
private int next;
/**
* Create a new string array reader.
*
* @param pStrings {@code String}s providing the character stream.
*/
public StringArrayReader(final String[] pStrings) {
super("");
Validate.notNull(pStrings, "strings");
finalLock = lock = pStrings; // NOTE: It's ok to sync on pStrings, as the
// reference can't change, only it's elements
strings = pStrings.clone(); // Defensive copy for content
nextReader();
}
protected final Reader nextReader() {
if (currentSting >= strings.length) {
current = new EmptyReader();
}
else {
current = new StringReader(strings[currentSting++]);
}
// NOTE: Reset next for every reader, and record marked reader in mark/reset methods!
next = 0;
return current;
}
/**
* Check to make sure that the stream has not been closed
*
* @throws IOException if the stream is closed
*/
protected final void ensureOpen() throws IOException {
if (strings == null) {
throw new IOException("Stream closed");
}
}
public void close() {
super.close();
strings = null;
current.close();
}
public void mark(int pReadLimit) throws IOException {
if (pReadLimit < 0){
throw new IllegalArgumentException("Read limit < 0");
}
synchronized (finalLock) {
ensureOpen();
mark = next;
markedString = currentSting;
current.mark(pReadLimit);
}
}
public void reset() throws IOException {
synchronized (finalLock) {
ensureOpen();
if (currentSting != markedString) {
currentSting = markedString - 1;
nextReader();
current.skip(mark);
}
else {
current.reset();
}
next = mark;
}
}
public boolean markSupported() {
return true;
}
public int read() throws IOException {
synchronized (finalLock) {
int read = current.read();
if (read < 0 && currentSting < strings.length) {
nextReader();
return read(); // In case of empty strings
}
next++;
return read;
}
}
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
synchronized (finalLock) {
int read = current.read(pBuffer, pOffset, pLength);
if (read < 0 && currentSting < strings.length) {
nextReader();
return read(pBuffer, pOffset, pLength); // In case of empty strings
}
next += read;
return read;
}
}
public boolean ready() throws IOException {
return current.ready();
}
public long skip(long pChars) throws IOException {
synchronized (finalLock) {
long skipped = current.skip(pChars);
if (skipped == 0 && currentSting < strings.length) {
nextReader();
return skip(pChars);
}
next += skipped;
return skipped;
}
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
/**
* StringArrayReader
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/StringArrayReader.java#2 $
*/
public class StringArrayReader extends StringReader {
private StringReader current;
private String[] strings;
protected final Object finalLock;
private int currentSting;
private int markedString;
private int mark;
private int next;
/**
* Create a new string array reader.
*
* @param pStrings {@code String}s providing the character stream.
*/
public StringArrayReader(final String[] pStrings) {
super("");
Validate.notNull(pStrings, "strings");
finalLock = lock = pStrings; // NOTE: It's ok to sync on pStrings, as the
// reference can't change, only it's elements
strings = pStrings.clone(); // Defensive copy for content
nextReader();
}
protected final Reader nextReader() {
if (currentSting >= strings.length) {
current = new EmptyReader();
}
else {
current = new StringReader(strings[currentSting++]);
}
// NOTE: Reset next for every reader, and record marked reader in mark/reset methods!
next = 0;
return current;
}
/**
* Check to make sure that the stream has not been closed
*
* @throws IOException if the stream is closed
*/
protected final void ensureOpen() throws IOException {
if (strings == null) {
throw new IOException("Stream closed");
}
}
public void close() {
super.close();
strings = null;
current.close();
}
public void mark(int pReadLimit) throws IOException {
if (pReadLimit < 0){
throw new IllegalArgumentException("Read limit < 0");
}
synchronized (finalLock) {
ensureOpen();
mark = next;
markedString = currentSting;
current.mark(pReadLimit);
}
}
public void reset() throws IOException {
synchronized (finalLock) {
ensureOpen();
if (currentSting != markedString) {
currentSting = markedString - 1;
nextReader();
current.skip(mark);
}
else {
current.reset();
}
next = mark;
}
}
public boolean markSupported() {
return true;
}
public int read() throws IOException {
synchronized (finalLock) {
int read = current.read();
if (read < 0 && currentSting < strings.length) {
nextReader();
return read(); // In case of empty strings
}
next++;
return read;
}
}
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
synchronized (finalLock) {
int read = current.read(pBuffer, pOffset, pLength);
if (read < 0 && currentSting < strings.length) {
nextReader();
return read(pBuffer, pOffset, pLength); // In case of empty strings
}
next += read;
return read;
}
}
public boolean ready() throws IOException {
return current.ready();
}
public long skip(long pChars) throws IOException {
synchronized (finalLock) {
long skipped = current.skip(pChars);
if (skipped == 0 && currentSting < strings.length) {
nextReader();
return skip(pChars);
}
next += skipped;
return skipped;
}
}
}

View File

@@ -1,137 +1,136 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* An {@code InputStream} reading up to a specified number of bytes from an
* underlying stream.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SubStream.java#2 $
*/
public final class SubStream extends FilterInputStream {
private long bytesLeft;
private int markLimit;
/**
* Creates a {@code SubStream} of the given {@code pStream}.
*
* @param pStream the underlying input stream
* @param pLength maximum number of bytes to read drom this stream
*/
public SubStream(final InputStream pStream, final long pLength) {
super(Validate.notNull(pStream, "stream"));
bytesLeft = pLength;
}
/**
* Marks this stream as closed.
* This implementation does <em>not</em> close the underlying stream.
*/
@Override
public void close() throws IOException {
// NOTE: Do not close the underlying stream
while (bytesLeft > 0) {
//noinspection ResultOfMethodCallIgnored
skip(bytesLeft);
}
}
@Override
public int available() throws IOException {
return (int) Math.min(super.available(), bytesLeft);
}
@Override
public void mark(int pReadLimit) {
super.mark(pReadLimit);// This either succeeds or does nothing...
markLimit = pReadLimit;
}
@Override
public void reset() throws IOException {
super.reset();// This either succeeds or throws IOException
bytesLeft += markLimit;
}
@Override
public int read() throws IOException {
if (bytesLeft-- <= 0) {
return -1;
}
return super.read();
}
@Override
public final int read(byte[] pBytes) throws IOException {
return read(pBytes, 0, pBytes.length);
}
@Override
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (bytesLeft <= 0) {
return -1;
}
int read = super.read(pBytes, pOffset, (int) findMaxLen(pLength));
bytesLeft = read < 0 ? 0 : bytesLeft - read;
return read;
}
/**
* Finds the maximum number of bytes we can read or skip, from this stream.
*
* @param pLength the requested length
* @return the maximum number of bytes to read
*/
private long findMaxLen(long pLength) {
if (bytesLeft < pLength) {
return (int) Math.max(bytesLeft, 0);
}
else {
return pLength;
}
}
@Override
public long skip(long pLength) throws IOException {
long skipped = super.skip(findMaxLen(pLength));// Skips 0 or more, never -1
bytesLeft -= skipped;
return skipped;
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* An {@code InputStream} reading up to a specified number of bytes from an
* underlying stream.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SubStream.java#2 $
*/
public final class SubStream extends FilterInputStream {
private long bytesLeft;
private int markLimit;
/**
* Creates a {@code SubStream} of the given {@code pStream}.
*
* @param pStream the underlying input stream
* @param pLength maximum number of bytes to read drom this stream
*/
public SubStream(final InputStream pStream, final long pLength) {
super(Validate.notNull(pStream, "stream"));
bytesLeft = pLength;
}
/**
* Marks this stream as closed.
* This implementation does <em>not</em> close the underlying stream.
*/
@Override
public void close() throws IOException {
// NOTE: Do not close the underlying stream
while (bytesLeft > 0) {
//noinspection ResultOfMethodCallIgnored
skip(bytesLeft);
}
}
@Override
public int available() throws IOException {
return (int) Math.min(super.available(), bytesLeft);
}
@Override
public void mark(int pReadLimit) {
super.mark(pReadLimit);// This either succeeds or does nothing...
markLimit = pReadLimit;
}
@Override
public void reset() throws IOException {
super.reset();// This either succeeds or throws IOException
bytesLeft += markLimit;
}
@Override
public int read() throws IOException {
if (bytesLeft-- <= 0) {
return -1;
}
return super.read();
}
@Override
public final int read(byte[] pBytes) throws IOException {
return read(pBytes, 0, pBytes.length);
}
@Override
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (bytesLeft <= 0) {
return -1;
}
int read = super.read(pBytes, pOffset, (int) findMaxLen(pLength));
bytesLeft = read < 0 ? 0 : bytesLeft - read;
return read;
}
/**
* Finds the maximum number of bytes we can read or skip, from this stream.
*
* @param pLength the requested length
* @return the maximum number of bytes to read
*/
private long findMaxLen(long pLength) {
if (bytesLeft < pLength) {
return (int) Math.max(bytesLeft, 0);
}
else {
return pLength;
}
}
@Override
public long skip(long pLength) throws IOException {
long skipped = super.skip(findMaxLen(pLength));// Skips 0 or more, never -1
bytesLeft -= skipped;
return skipped;
}
}

View File

@@ -1,107 +1,106 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.util.StringTokenIterator;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
/**
* UnixFileSystem
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/UnixFileSystem.java#1 $
*/
final class UnixFileSystem extends FileSystem {
long getFreeSpace(File pPath) {
try {
return getNumber(pPath, 3);
}
catch (IOException e) {
return 0l;
}
}
long getTotalSpace(File pPath) {
try {
return getNumber(pPath, 5);
}
catch (IOException e) {
return 0l;
}
}
private long getNumber(File pPath, int pIndex) throws IOException {
// TODO: Test on other platforms
// Tested on Mac OS X, CygWin
BufferedReader reader = exec(new String[] {"df", "-k", pPath.getAbsolutePath()});
String last = null;
String line;
try {
while ((line = reader.readLine()) != null) {
last = line;
}
}
finally {
FileUtil.close(reader);
}
if (last != null) {
String blocks = null;
StringTokenIterator tokens = new StringTokenIterator(last, " ", StringTokenIterator.REVERSE);
int count = 0;
// We want the 3rd last token
while (count < pIndex && tokens.hasNext()) {
blocks = tokens.nextToken();
count++;
}
if (blocks != null) {
try {
return Long.parseLong(blocks) * 1024L;
}
catch (NumberFormatException ignore) {
// Ignore
}
}
}
return 0l;
}
String getName() {
return "Unix";
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.util.StringTokenIterator;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
/**
* UnixFileSystem
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/UnixFileSystem.java#1 $
*/
final class UnixFileSystem extends FileSystem {
long getFreeSpace(File pPath) {
try {
return getNumber(pPath, 3);
}
catch (IOException e) {
return 0l;
}
}
long getTotalSpace(File pPath) {
try {
return getNumber(pPath, 5);
}
catch (IOException e) {
return 0l;
}
}
private long getNumber(File pPath, int pIndex) throws IOException {
// TODO: Test on other platforms
// Tested on Mac OS X, CygWin
BufferedReader reader = exec(new String[] {"df", "-k", pPath.getAbsolutePath()});
String last = null;
String line;
try {
while ((line = reader.readLine()) != null) {
last = line;
}
}
finally {
FileUtil.close(reader);
}
if (last != null) {
String blocks = null;
StringTokenIterator tokens = new StringTokenIterator(last, " ", StringTokenIterator.REVERSE);
int count = 0;
// We want the 3rd last token
while (count < pIndex && tokens.hasNext()) {
blocks = tokens.nextToken();
count++;
}
if (blocks != null) {
try {
return Long.parseLong(blocks) * 1024L;
}
catch (NumberFormatException ignore) {
// Ignore
}
}
}
return 0l;
}
String getName() {
return "Unix";
}
}

View File

@@ -1,195 +1,194 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
/**
* Win32File
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32File.java#2 $
*/
final class Win32File extends File {
private final static boolean IS_WINDOWS = isWindows();
private static boolean isWindows() {
try {
String os = System.getProperty("os.name");
return os.toLowerCase().indexOf("windows") >= 0;
}
catch (Throwable t) {
// Ignore
}
return false;
}
private Win32File(File pPath) {
super(pPath.getPath());
}
public static void main(String[] pArgs) {
int argIdx = 0;
boolean recursive = false;
while (pArgs.length > argIdx + 1 && pArgs[argIdx].charAt(0) == '-' && pArgs[argIdx].length() > 1) {
if (pArgs[argIdx].charAt(1) == 'R' || pArgs[argIdx].equals("--recursive")) {
recursive = true;
}
else {
System.err.println("Unknown option: " + pArgs[argIdx]);
}
argIdx++;
}
File file = wrap(new File(pArgs[argIdx]));
System.out.println("file: " + file);
System.out.println("file.getClass(): " + file.getClass());
listFiles(file, 0, recursive);
}
private static void listFiles(File pFile, int pLevel, boolean pRecursive) {
if (pFile.isDirectory()) {
File[] files = pFile.listFiles();
for (int l = 0; l < pLevel; l++) {
System.out.print(" ");
}
System.out.println("Contents of " + pFile + ": ");
for (File file : files) {
for (int l = 0; l < pLevel; l++) {
System.out.print(" ");
}
System.out.println(" " + file);
if (pRecursive) {
listFiles(file, pLevel + 1, pLevel < 4);
}
}
}
}
/**
* Wraps a {@code File} object pointing to a Windows symbolic link
* ({@code .lnk} file) in a {@code Win32Lnk}.
* If the operating system is not Windows, the
* {@code pPath} parameter is returned unwrapped.
*
* @param pPath any path, possibly pointing to a Windows symbolic link file.
* May be {@code null}, in which case {@code null} is returned.
*
* @return a new {@code Win32Lnk} object if the current os is Windows, and
* the file is a Windows symbolic link ({@code .lnk} file), otherwise
* {@code pPath}
*/
public static File wrap(final File pPath) {
if (pPath == null) {
return null;
}
if (IS_WINDOWS) {
// Don't wrap if allready wrapped
if (pPath instanceof Win32File || pPath instanceof Win32Lnk) {
return pPath;
}
if (pPath.exists() && pPath.getName().endsWith(".lnk")) {
// If Win32 .lnk, let's wrap
try {
return new Win32Lnk(pPath);
}
catch (IOException e) {
// TODO: FixMe!
e.printStackTrace();
}
}
// Wwrap even if not a .lnk, as the listFiles() methods etc,
// could potentially return .lnk's, that we want to wrap later...
return new Win32File(pPath);
}
return pPath;
}
/**
* Wraps a {@code File} array, possibly pointing to Windows symbolic links
* ({@code .lnk} files) in {@code Win32Lnk}s.
*
* @param pPaths an array of {@code File}s, possibly pointing to Windows
* symbolic link files.
* May be {@code null}, in which case {@code null} is returned.
*
* @return {@code pPaths}, with any {@code File} representing a Windows
* symbolic link ({@code .lnk} file) wrapped in a {@code Win32Lnk}.
*/
public static File[] wrap(File[] pPaths) {
if (IS_WINDOWS) {
for (int i = 0; pPaths != null && i < pPaths.length; i++) {
pPaths[i] = wrap(pPaths[i]);
}
}
return pPaths;
}
// File overrides
@Override
public File getAbsoluteFile() {
return wrap(super.getAbsoluteFile());
}
@Override
public File getCanonicalFile() throws IOException {
return wrap(super.getCanonicalFile());
}
@Override
public File getParentFile() {
return wrap(super.getParentFile());
}
@Override
public File[] listFiles() {
return wrap(super.listFiles());
}
@Override
public File[] listFiles(FileFilter filter) {
return wrap(super.listFiles(filter));
}
@Override
public File[] listFiles(FilenameFilter filter) {
return wrap(super.listFiles(filter));
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
/**
* Win32File
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32File.java#2 $
*/
final class Win32File extends File {
private final static boolean IS_WINDOWS = isWindows();
private static boolean isWindows() {
try {
String os = System.getProperty("os.name");
return os.toLowerCase().indexOf("windows") >= 0;
}
catch (Throwable t) {
// Ignore
}
return false;
}
private Win32File(File pPath) {
super(pPath.getPath());
}
public static void main(String[] pArgs) {
int argIdx = 0;
boolean recursive = false;
while (pArgs.length > argIdx + 1 && pArgs[argIdx].charAt(0) == '-' && pArgs[argIdx].length() > 1) {
if (pArgs[argIdx].charAt(1) == 'R' || pArgs[argIdx].equals("--recursive")) {
recursive = true;
}
else {
System.err.println("Unknown option: " + pArgs[argIdx]);
}
argIdx++;
}
File file = wrap(new File(pArgs[argIdx]));
System.out.println("file: " + file);
System.out.println("file.getClass(): " + file.getClass());
listFiles(file, 0, recursive);
}
private static void listFiles(File pFile, int pLevel, boolean pRecursive) {
if (pFile.isDirectory()) {
File[] files = pFile.listFiles();
for (int l = 0; l < pLevel; l++) {
System.out.print(" ");
}
System.out.println("Contents of " + pFile + ": ");
for (File file : files) {
for (int l = 0; l < pLevel; l++) {
System.out.print(" ");
}
System.out.println(" " + file);
if (pRecursive) {
listFiles(file, pLevel + 1, pLevel < 4);
}
}
}
}
/**
* Wraps a {@code File} object pointing to a Windows symbolic link
* ({@code .lnk} file) in a {@code Win32Lnk}.
* If the operating system is not Windows, the
* {@code pPath} parameter is returned unwrapped.
*
* @param pPath any path, possibly pointing to a Windows symbolic link file.
* May be {@code null}, in which case {@code null} is returned.
*
* @return a new {@code Win32Lnk} object if the current os is Windows, and
* the file is a Windows symbolic link ({@code .lnk} file), otherwise
* {@code pPath}
*/
public static File wrap(final File pPath) {
if (pPath == null) {
return null;
}
if (IS_WINDOWS) {
// Don't wrap if allready wrapped
if (pPath instanceof Win32File || pPath instanceof Win32Lnk) {
return pPath;
}
if (pPath.exists() && pPath.getName().endsWith(".lnk")) {
// If Win32 .lnk, let's wrap
try {
return new Win32Lnk(pPath);
}
catch (IOException e) {
// TODO: FixMe!
e.printStackTrace();
}
}
// Wwrap even if not a .lnk, as the listFiles() methods etc,
// could potentially return .lnk's, that we want to wrap later...
return new Win32File(pPath);
}
return pPath;
}
/**
* Wraps a {@code File} array, possibly pointing to Windows symbolic links
* ({@code .lnk} files) in {@code Win32Lnk}s.
*
* @param pPaths an array of {@code File}s, possibly pointing to Windows
* symbolic link files.
* May be {@code null}, in which case {@code null} is returned.
*
* @return {@code pPaths}, with any {@code File} representing a Windows
* symbolic link ({@code .lnk} file) wrapped in a {@code Win32Lnk}.
*/
public static File[] wrap(File[] pPaths) {
if (IS_WINDOWS) {
for (int i = 0; pPaths != null && i < pPaths.length; i++) {
pPaths[i] = wrap(pPaths[i]);
}
}
return pPaths;
}
// File overrides
@Override
public File getAbsoluteFile() {
return wrap(super.getAbsoluteFile());
}
@Override
public File getCanonicalFile() throws IOException {
return wrap(super.getCanonicalFile());
}
@Override
public File getParentFile() {
return wrap(super.getParentFile());
}
@Override
public File[] listFiles() {
return wrap(super.listFiles());
}
@Override
public File[] listFiles(FileFilter filter) {
return wrap(super.listFiles(filter));
}
@Override
public File[] listFiles(FilenameFilter filter) {
return wrap(super.listFiles(filter));
}
}

View File

@@ -1,92 +1,91 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
/**
* WindowsFileSystem
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32FileSystem.java#2 $
*/
final class Win32FileSystem extends FileSystem {
public long getFreeSpace(File pPath) {
try {
// Windows version
// TODO: Test on W2K/95/98/etc... (tested on XP)
BufferedReader reader = exec(new String[] {"CMD.EXE", "/C", "DIR", "/-C", pPath.getAbsolutePath()});
String last = null;
String line;
try {
while ((line = reader.readLine()) != null) {
last = line;
}
}
finally {
FileUtil.close(reader);
}
if (last != null) {
int end = last.lastIndexOf(" bytes free");
int start = last.lastIndexOf(' ', end - 1);
if (start >= 0 && end >= 0) {
try {
return Long.parseLong(last.substring(start + 1, end));
}
catch (NumberFormatException ignore) {
// Ignore
}
}
}
}
catch (IOException ignore) {
// Ignore
}
return 0l;
}
long getTotalSpace(File pPath) {
// TODO: Implement, probably need some JNI stuff...
// Distribute df.exe and execute from temp!? ;-)
return getFreeSpace(pPath);
}
String getName() {
return "Win32";
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
/**
* WindowsFileSystem
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32FileSystem.java#2 $
*/
final class Win32FileSystem extends FileSystem {
public long getFreeSpace(File pPath) {
try {
// Windows version
// TODO: Test on W2K/95/98/etc... (tested on XP)
BufferedReader reader = exec(new String[] {"CMD.EXE", "/C", "DIR", "/-C", pPath.getAbsolutePath()});
String last = null;
String line;
try {
while ((line = reader.readLine()) != null) {
last = line;
}
}
finally {
FileUtil.close(reader);
}
if (last != null) {
int end = last.lastIndexOf(" bytes free");
int start = last.lastIndexOf(' ', end - 1);
if (start >= 0 && end >= 0) {
try {
return Long.parseLong(last.substring(start + 1, end));
}
catch (NumberFormatException ignore) {
// Ignore
}
}
}
}
catch (IOException ignore) {
// Ignore
}
return 0l;
}
long getTotalSpace(File pPath) {
// TODO: Implement, probably need some JNI stuff...
// Distribute df.exe and execute from temp!? ;-)
return getFreeSpace(pPath);
}
String getName() {
return "Win32";
}
}

View File

@@ -1,475 +1,477 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.*;
import java.util.Arrays;
/**
* A {@code File} implementation that resolves the Windows {@code .lnk} files as symbolic links.
* <p/>
* This class is based on example code from
* <a href="http://www.oreilly.com/catalog/swinghks/index.html">Swing Hacks</a>,
* By Joshua Marinacci, Chris Adamson (O'Reilly, ISBN: 0-596-00907-0), Hack 30.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32Lnk.java#2 $
*/
final class Win32Lnk extends File {
private final static byte[] LNK_MAGIC = {
'L', 0x00, 0x00, 0x00, // Magic
};
private final static byte[] LNK_GUID = {
0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Shell Link GUID
(byte) 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'F'
};
private final File target;
private static final int FLAG_ITEM_ID_LIST = 0x01;
private static final int FLAG_FILE_LOC_INFO = 0x02;
private static final int FLAG_DESC_STRING = 0x04;
private static final int FLAG_REL_PATH_STRING = 0x08;
private static final int FLAG_WORKING_DIRECTORY = 0x10;
private static final int FLAG_COMMAND_LINE_ARGS = 0x20;
private static final int FLAG_ICON_FILENAME = 0x40;
private static final int FLAG_ADDITIONAL_INFO = 0x80;
private Win32Lnk(final String pPath) throws IOException {
super(pPath);
File target = parse(this);
if (target == this) {
// NOTE: This is a workaround
// target = this causes infinite loops in some methods
target = new File(pPath);
}
this.target = target;
}
Win32Lnk(final File pPath) throws IOException {
this(pPath.getPath());
}
/**
* Parses a {@code .lnk} file to find the real file.
*
* @param pPath the path to the {@code .lnk} file
* @return a new file object that
* @throws java.io.IOException if the {@code .lnk} cannot be parsed
*/
static File parse(final File pPath) throws IOException {
if (!pPath.getName().endsWith(".lnk")) {
return pPath;
}
File result = pPath;
LittleEndianDataInputStream in = new LittleEndianDataInputStream(new BufferedInputStream(new FileInputStream(pPath)));
try {
byte[] magic = new byte[4];
in.readFully(magic);
byte[] guid = new byte[16];
in.readFully(guid);
if (!(Arrays.equals(LNK_MAGIC, magic) && Arrays.equals(LNK_GUID, guid))) {
//System.out.println("Not a symlink");
// Not a symlink
return pPath;
}
// Get the flags
int flags = in.readInt();
//System.out.println("flags: " + Integer.toBinaryString(flags & 0xff));
// Get to the file settings
/*int attributes = */in.readInt();
// File attributes
// 0 Target is read only.
// 1 Target is hidden.
// 2 Target is a system file.
// 3 Target is a volume label. (Not possible)
// 4 Target is a directory.
// 5 Target has been modified since last backup. (archive)
// 6 Target is encrypted (NTFS EFS)
// 7 Target is Normal??
// 8 Target is temporary.
// 9 Target is a sparse file.
// 10 Target has reparse point data.
// 11 Target is compressed.
// 12 Target is offline.
//System.out.println("attributes: " + Integer.toBinaryString(attributes));
// NOTE: Cygwin .lnks are not directory links, can't rely on this.. :-/
in.skipBytes(48); // TODO: Make sense of this data...
// Skipped data:
// long time 1 (creation)
// long time 2 (modification)
// long time 3 (last access)
// int file length
// int icon number
// int ShowVnd value
// int hotkey
// int, int - unknown: 0,0
// If the shell settings are present, skip them
if ((flags & FLAG_ITEM_ID_LIST) != 0) {
// Shell Item Id List present
//System.out.println("Shell Item Id List present");
int shellLen = in.readShort(); // Short
//System.out.println("shellLen: " + shellLen);
// TODO: Probably need to parse this data, to determine
// Cygwin folders...
/*
int read = 2;
int itemLen = in.readShort();
while (itemLen > 0) {
System.out.println("--> ITEM: " + itemLen);
BufferedReader reader = new BufferedReader(new InputStreamReader(new SubStream(in, itemLen - 2)));
//byte[] itemBytes = new byte[itemLen - 2]; // NOTE: Lenght included
//in.readFully(itemBytes);
String item = reader.readLine();
System.out.println("item: \"" + item + "\"");
itemLen = in.readShort();
read += itemLen;
}
System.out.println("read: " + read);
*/
in.skipBytes(shellLen);
}
if ((flags & FLAG_FILE_LOC_INFO) != 0) {
// File Location Info Table present
//System.out.println("File Location Info Table present");
// 0h 1 dword This is the total length of this structure and all following data
// 4h 1 dword This is a pointer to first offset after this structure. 1Ch
// 8h 1 dword Flags
// Ch 1 dword Offset of local volume info
// 10h 1 dword Offset of base pathname on local system
// 14h 1 dword Offset of network volume info
// 18h 1 dword Offset of remaining pathname
// Flags:
// Bit Meaning
// 0 Available on a local volume
// 1 Available on a network share
// TODO: Make sure the path is on a local disk, etc..
int tableLen = in.readInt(); // Int
//System.out.println("tableLen: " + tableLen);
in.readInt(); // Skip
int locFlags = in.readInt();
//System.out.println("locFlags: " + Integer.toBinaryString(locFlags));
if ((locFlags & 0x01) != 0) {
//System.out.println("Available local");
}
if ((locFlags & 0x02) != 0) {
//System.err.println("Available on network path");
}
// Get the local volume and local system values
in.skipBytes(4); // TODO: see above for structure
int localSysOff = in.readInt();
//System.out.println("localSysOff: " + localSysOff);
in.skipBytes(localSysOff - 20); // Relative to start of chunk
byte[] pathBytes = new byte[tableLen - localSysOff - 1];
in.readFully(pathBytes, 0, pathBytes.length);
String path = new String(pathBytes, 0, pathBytes.length - 1);
/*
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
byte read;
// Read bytes until the null (0) character
while (true) {
read = in.readByte();
if (read == 0) {
break;
}
bytes.write(read & 0xff);
}
String path = new String(bytes.toByteArray(), 0, bytes.size());
//*/
// Recurse to end of link chain
// TODO: This may cause endless loop if cyclic chain...
//System.out.println("path: \"" + path + "\"");
try {
result = parse(new File(path));
}
catch (StackOverflowError e) {
throw new IOException("Cannot resolve cyclic link: " + e.getMessage());
}
}
if ((flags & FLAG_DESC_STRING) != 0) {
// Description String present, skip it.
//System.out.println("Description String present");
// The string length is the first word which must also be skipped.
int descLen = in.readShort();
//System.out.println("descLen: " + descLen);
byte[] descBytes = new byte[descLen];
in.readFully(descBytes, 0, descLen);
//String desc = new String(descBytes, 0, descLen);
//System.out.println("desc: " + desc);
}
if ((flags & FLAG_REL_PATH_STRING) != 0) {
// Relative Path String present
//System.out.println("Relative Path String present");
// The string length is the first word which must also be skipped.
int pathLen = in.readShort();
//System.out.println("pathLen: " + pathLen);
byte[] pathBytes = new byte[pathLen];
in.readFully(pathBytes, 0, pathLen);
String path = new String(pathBytes, 0, pathLen);
// TODO: This may cause endless loop if cyclic chain...
//System.out.println("path: \"" + path + "\"");
if (result == pPath) {
try {
result = parse(new File(pPath.getParentFile(), path));
}
catch (StackOverflowError e) {
throw new IOException("Cannot resolve cyclic link: " + e.getMessage());
}
}
}
if ((flags & FLAG_WORKING_DIRECTORY) != 0) {
//System.out.println("Working Directory present");
}
if ((flags & FLAG_COMMAND_LINE_ARGS) != 0) {
//System.out.println("Command Line Arguments present");
// NOTE: This means this .lnk is not a folder, don't follow
result = pPath;
}
if ((flags & FLAG_ICON_FILENAME) != 0) {
//System.out.println("Icon Filename present");
}
if ((flags & FLAG_ADDITIONAL_INFO) != 0) {
//System.out.println("Additional Info present");
}
}
finally {
in.close();
}
return result;
}
/*
private static String getNullDelimitedString(byte[] bytes, int off) {
int len = 0;
// Count bytes until the null (0) character
while (true) {
if (bytes[off + len] == 0) {
break;
}
len++;
}
System.err.println("--> " + len);
return new String(bytes, off, len);
}
*/
/**
* Converts two bytes into a short.
* <p/>
* NOTE: this is little endian because it's for an
* Intel only OS
*
* @ param bytes
* @ param off
* @return the bytes as a short.
*/
/*
private static int bytes2short(byte[] bytes, int off) {
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
}
*/
public File getTarget() {
return target;
}
// java.io.File overrides below
@Override
public boolean isDirectory() {
return target.isDirectory();
}
@Override
public boolean canRead() {
return target.canRead();
}
@Override
public boolean canWrite() {
return target.canWrite();
}
// NOTE: equals is implemented using compareto == 0
/*
public int compareTo(File pathname) {
// TODO: Verify this
// Probably not a good idea, as it IS NOT THE SAME file
// It's probably better to not override
return target.compareTo(pathname);
}
*/
// Should probably never allow creating a new .lnk
// public boolean createNewFile() throws IOException
// Deletes only the .lnk
// public boolean delete() {
//public void deleteOnExit() {
@Override
public boolean exists() {
return target.exists();
}
// A .lnk may be absolute
//public File getAbsoluteFile() {
//public String getAbsolutePath() {
// Theses should be resolved according to the API (for Unix).
@Override
public File getCanonicalFile() throws IOException {
return target.getCanonicalFile();
}
@Override
public String getCanonicalPath() throws IOException {
return target.getCanonicalPath();
}
//public String getName() {
// I guess the parent should be the parent of the .lnk, not the target
//public String getParent() {
//public File getParentFile() {
// public boolean isAbsolute() {
@Override
public boolean isFile() {
return target.isFile();
}
@Override
public boolean isHidden() {
return target.isHidden();
}
@Override
public long lastModified() {
return target.lastModified();
}
@Override
public long length() {
return target.length();
}
@Override
public String[] list() {
return target.list();
}
@Override
public String[] list(final FilenameFilter filter) {
return target.list(filter);
}
@Override
public File[] listFiles() {
return Win32File.wrap(target.listFiles());
}
@Override
public File[] listFiles(final FileFilter filter) {
return Win32File.wrap(target.listFiles(filter));
}
@Override
public File[] listFiles(final FilenameFilter filter) {
return Win32File.wrap(target.listFiles(filter));
}
// Makes no sense, does it?
//public boolean mkdir() {
//public boolean mkdirs() {
// Only rename the lnk
//public boolean renameTo(File dest) {
@Override
public boolean setLastModified(long time) {
return target.setLastModified(time);
}
@Override
public boolean setReadOnly() {
return target.setReadOnly();
}
@Override
public String toString() {
if (target.equals(this)) {
return super.toString();
}
return super.toString() + " -> " + target.toString();
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import java.io.*;
import java.util.Arrays;
/**
* A {@code File} implementation that resolves the Windows {@code .lnk} files as symbolic links.
* <p>
* This class is based on example code from
* <a href="http://www.oreilly.com/catalog/swinghks/index.html">Swing Hacks</a>,
* By Joshua Marinacci, Chris Adamson (O'Reilly, ISBN: 0-596-00907-0), Hack 30.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32Lnk.java#2 $
*/
final class Win32Lnk extends File {
private final static byte[] LNK_MAGIC = {
'L', 0x00, 0x00, 0x00, // Magic
};
private final static byte[] LNK_GUID = {
0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Shell Link GUID
(byte) 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'F'
};
private final File target;
private static final int FLAG_ITEM_ID_LIST = 0x01;
private static final int FLAG_FILE_LOC_INFO = 0x02;
private static final int FLAG_DESC_STRING = 0x04;
private static final int FLAG_REL_PATH_STRING = 0x08;
private static final int FLAG_WORKING_DIRECTORY = 0x10;
private static final int FLAG_COMMAND_LINE_ARGS = 0x20;
private static final int FLAG_ICON_FILENAME = 0x40;
private static final int FLAG_ADDITIONAL_INFO = 0x80;
private Win32Lnk(final String pPath) throws IOException {
super(pPath);
File target = parse(this);
if (target == this) {
// NOTE: This is a workaround
// target = this causes infinite loops in some methods
target = new File(pPath);
}
this.target = target;
}
Win32Lnk(final File pPath) throws IOException {
this(pPath.getPath());
}
/**
* Parses a {@code .lnk} file to find the real file.
*
* @param pPath the path to the {@code .lnk} file
* @return a new file object that
* @throws java.io.IOException if the {@code .lnk} cannot be parsed
*/
static File parse(final File pPath) throws IOException {
if (!pPath.getName().endsWith(".lnk")) {
return pPath;
}
File result = pPath;
LittleEndianDataInputStream in = new LittleEndianDataInputStream(new BufferedInputStream(new FileInputStream(pPath)));
try {
byte[] magic = new byte[4];
in.readFully(magic);
byte[] guid = new byte[16];
in.readFully(guid);
if (!(Arrays.equals(LNK_MAGIC, magic) && Arrays.equals(LNK_GUID, guid))) {
//System.out.println("Not a symlink");
// Not a symlink
return pPath;
}
// Get the flags
int flags = in.readInt();
//System.out.println("flags: " + Integer.toBinaryString(flags & 0xff));
// Get to the file settings
/*int attributes = */in.readInt();
// File attributes
// 0 Target is read only.
// 1 Target is hidden.
// 2 Target is a system file.
// 3 Target is a volume label. (Not possible)
// 4 Target is a directory.
// 5 Target has been modified since last backup. (archive)
// 6 Target is encrypted (NTFS EFS)
// 7 Target is Normal??
// 8 Target is temporary.
// 9 Target is a sparse file.
// 10 Target has reparse point data.
// 11 Target is compressed.
// 12 Target is offline.
//System.out.println("attributes: " + Integer.toBinaryString(attributes));
// NOTE: Cygwin .lnks are not directory links, can't rely on this.. :-/
in.skipBytes(48); // TODO: Make sense of this data...
// Skipped data:
// long time 1 (creation)
// long time 2 (modification)
// long time 3 (last access)
// int file length
// int icon number
// int ShowVnd value
// int hotkey
// int, int - unknown: 0,0
// If the shell settings are present, skip them
if ((flags & FLAG_ITEM_ID_LIST) != 0) {
// Shell Item Id List present
//System.out.println("Shell Item Id List present");
int shellLen = in.readShort(); // Short
//System.out.println("shellLen: " + shellLen);
// TODO: Probably need to parse this data, to determine
// Cygwin folders...
/*
int read = 2;
int itemLen = in.readShort();
while (itemLen > 0) {
System.out.println("--> ITEM: " + itemLen);
BufferedReader reader = new BufferedReader(new InputStreamReader(new SubStream(in, itemLen - 2)));
//byte[] itemBytes = new byte[itemLen - 2]; // NOTE: Lenght included
//in.readFully(itemBytes);
String item = reader.readLine();
System.out.println("item: \"" + item + "\"");
itemLen = in.readShort();
read += itemLen;
}
System.out.println("read: " + read);
*/
in.skipBytes(shellLen);
}
if ((flags & FLAG_FILE_LOC_INFO) != 0) {
// File Location Info Table present
//System.out.println("File Location Info Table present");
// 0h 1 dword This is the total length of this structure and all following data
// 4h 1 dword This is a pointer to first offset after this structure. 1Ch
// 8h 1 dword Flags
// Ch 1 dword Offset of local volume info
// 10h 1 dword Offset of base pathname on local system
// 14h 1 dword Offset of network volume info
// 18h 1 dword Offset of remaining pathname
// Flags:
// Bit Meaning
// 0 Available on a local volume
// 1 Available on a network share
// TODO: Make sure the path is on a local disk, etc..
int tableLen = in.readInt(); // Int
//System.out.println("tableLen: " + tableLen);
in.readInt(); // Skip
int locFlags = in.readInt();
//System.out.println("locFlags: " + Integer.toBinaryString(locFlags));
if ((locFlags & 0x01) != 0) {
//System.out.println("Available local");
}
if ((locFlags & 0x02) != 0) {
//System.err.println("Available on network path");
}
// Get the local volume and local system values
in.skipBytes(4); // TODO: see above for structure
int localSysOff = in.readInt();
//System.out.println("localSysOff: " + localSysOff);
in.skipBytes(localSysOff - 20); // Relative to start of chunk
byte[] pathBytes = new byte[tableLen - localSysOff - 1];
in.readFully(pathBytes, 0, pathBytes.length);
String path = new String(pathBytes, 0, pathBytes.length - 1);
/*
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
byte read;
// Read bytes until the null (0) character
while (true) {
read = in.readByte();
if (read == 0) {
break;
}
bytes.write(read & 0xff);
}
String path = new String(bytes.toByteArray(), 0, bytes.size());
//*/
// Recurse to end of link chain
// TODO: This may cause endless loop if cyclic chain...
//System.out.println("path: \"" + path + "\"");
try {
result = parse(new File(path));
}
catch (StackOverflowError e) {
throw new IOException("Cannot resolve cyclic link: " + e.getMessage());
}
}
if ((flags & FLAG_DESC_STRING) != 0) {
// Description String present, skip it.
//System.out.println("Description String present");
// The string length is the first word which must also be skipped.
int descLen = in.readShort();
//System.out.println("descLen: " + descLen);
byte[] descBytes = new byte[descLen];
in.readFully(descBytes, 0, descLen);
//String desc = new String(descBytes, 0, descLen);
//System.out.println("desc: " + desc);
}
if ((flags & FLAG_REL_PATH_STRING) != 0) {
// Relative Path String present
//System.out.println("Relative Path String present");
// The string length is the first word which must also be skipped.
int pathLen = in.readShort();
//System.out.println("pathLen: " + pathLen);
byte[] pathBytes = new byte[pathLen];
in.readFully(pathBytes, 0, pathLen);
String path = new String(pathBytes, 0, pathLen);
// TODO: This may cause endless loop if cyclic chain...
//System.out.println("path: \"" + path + "\"");
if (result == pPath) {
try {
result = parse(new File(pPath.getParentFile(), path));
}
catch (StackOverflowError e) {
throw new IOException("Cannot resolve cyclic link: " + e.getMessage());
}
}
}
if ((flags & FLAG_WORKING_DIRECTORY) != 0) {
//System.out.println("Working Directory present");
}
if ((flags & FLAG_COMMAND_LINE_ARGS) != 0) {
//System.out.println("Command Line Arguments present");
// NOTE: This means this .lnk is not a folder, don't follow
result = pPath;
}
if ((flags & FLAG_ICON_FILENAME) != 0) {
//System.out.println("Icon Filename present");
}
if ((flags & FLAG_ADDITIONAL_INFO) != 0) {
//System.out.println("Additional Info present");
}
}
finally {
in.close();
}
return result;
}
/*
private static String getNullDelimitedString(byte[] bytes, int off) {
int len = 0;
// Count bytes until the null (0) character
while (true) {
if (bytes[off + len] == 0) {
break;
}
len++;
}
System.err.println("--> " + len);
return new String(bytes, off, len);
}
*/
/**
* Converts two bytes into a short.
* <p>
* NOTE: this is little endian because it's for an
* Intel only OS
* </p>
*
* @ param bytes
* @ param off
* @return the bytes as a short.
*/
/*
private static int bytes2short(byte[] bytes, int off) {
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
}
*/
public File getTarget() {
return target;
}
// java.io.File overrides below
@Override
public boolean isDirectory() {
return target.isDirectory();
}
@Override
public boolean canRead() {
return target.canRead();
}
@Override
public boolean canWrite() {
return target.canWrite();
}
// NOTE: equals is implemented using compareto == 0
/*
public int compareTo(File pathname) {
// TODO: Verify this
// Probably not a good idea, as it IS NOT THE SAME file
// It's probably better to not override
return target.compareTo(pathname);
}
*/
// Should probably never allow creating a new .lnk
// public boolean createNewFile() throws IOException
// Deletes only the .lnk
// public boolean delete() {
//public void deleteOnExit() {
@Override
public boolean exists() {
return target.exists();
}
// A .lnk may be absolute
//public File getAbsoluteFile() {
//public String getAbsolutePath() {
// Theses should be resolved according to the API (for Unix).
@Override
public File getCanonicalFile() throws IOException {
return target.getCanonicalFile();
}
@Override
public String getCanonicalPath() throws IOException {
return target.getCanonicalPath();
}
//public String getName() {
// I guess the parent should be the parent of the .lnk, not the target
//public String getParent() {
//public File getParentFile() {
// public boolean isAbsolute() {
@Override
public boolean isFile() {
return target.isFile();
}
@Override
public boolean isHidden() {
return target.isHidden();
}
@Override
public long lastModified() {
return target.lastModified();
}
@Override
public long length() {
return target.length();
}
@Override
public String[] list() {
return target.list();
}
@Override
public String[] list(final FilenameFilter filter) {
return target.list(filter);
}
@Override
public File[] listFiles() {
return Win32File.wrap(target.listFiles());
}
@Override
public File[] listFiles(final FileFilter filter) {
return Win32File.wrap(target.listFiles(filter));
}
@Override
public File[] listFiles(final FilenameFilter filter) {
return Win32File.wrap(target.listFiles(filter));
}
// Makes no sense, does it?
//public boolean mkdir() {
//public boolean mkdirs() {
// Only rename the lnk
//public boolean renameTo(File dest) {
@Override
public boolean setLastModified(long time) {
return target.setLastModified(time);
}
@Override
public boolean setReadOnly() {
return target.setReadOnly();
}
@Override
public String toString() {
if (target.equals(this)) {
return super.toString();
}
return super.toString() + " -> " + target.toString();
}
}

View File

@@ -1,239 +1,240 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.DateUtil;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
/**
* Wraps a {@code Writer} in an {@code OutputStream}.
* <p/>
* <em>Instances of this class are not thread-safe.</em>
* <p/>
* <em>NOTE: This class is probably not the right way of solving your problem,
* however it might prove useful in JSPs etc.
* If possible, it's always better to use the {@code Writer}'s underlying
* {@code OutputStream}, or wrap it's native backing.
* </em>
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/WriterOutputStream.java#2 $
*/
public class WriterOutputStream extends OutputStream {
protected Writer writer;
final protected Decoder decoder;
final ByteArrayOutputStream bufferStream = new FastByteArrayOutputStream(1024);
private volatile boolean isFlushing = false; // Ugly but critical...
private static final boolean NIO_AVAILABLE = isNIOAvailable();
private static boolean isNIOAvailable() {
try {
Class.forName("java.nio.charset.Charset");
return true;
}
catch (Throwable t) {
// Ignore
}
return false;
}
public WriterOutputStream(final Writer pWriter, final String pCharset) {
writer = pWriter;
decoder = getDecoder(pCharset);
}
public WriterOutputStream(final Writer pWriter) {
this(pWriter, null);
}
private static Decoder getDecoder(final String pCharset) {
// NOTE: The CharsetDecoder is typically 10-20% faster than
// StringDecoder according to my tests
// StringEncoder is horribly slow on 1.2 systems, but there's no
// alternative...
if (NIO_AVAILABLE) {
return new CharsetDecoder(pCharset);
}
return new StringDecoder(pCharset);
}
@Override
public void close() throws IOException {
flush();
writer.close();
writer = null;
}
@Override
public void flush() throws IOException {
flushBuffer();
writer.flush();
}
@Override
public final void write(byte[] pBytes) throws IOException {
if (pBytes == null) {
throw new NullPointerException("bytes == null");
}
write(pBytes, 0, pBytes.length);
}
@Override
public final void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
flushBuffer();
decoder.decodeTo(writer, pBytes, pOffset, pLength);
}
@Override
public final void write(int pByte) {
// TODO: Is it possible to know if this is a good place in the stream to
// flush? It might be in the middle of a multi-byte encoded character..
bufferStream.write(pByte);
}
private void flushBuffer() throws IOException {
if (!isFlushing && bufferStream.size() > 0) {
isFlushing = true;
bufferStream.writeTo(this); // NOTE: Avoids cloning buffer array
bufferStream.reset();
isFlushing = false;
}
}
///////////////////////////////////////////////////////////////////////////
public static void main(String[] pArgs) throws IOException {
int iterations = 1000000;
byte[] bytes = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> klashf lkash ljah lhaaklhghdfgu ksd".getBytes("UTF-8");
Decoder d;
long start;
long time;
Writer sink = new PrintWriter(new NullOutputStream());
StringWriter writer;
String str;
d = new StringDecoder("UTF-8");
for (int i = 0; i < 10000; i++) {
d.decodeTo(sink, bytes, 0, bytes.length);
}
start = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
d.decodeTo(sink, bytes, 0, bytes.length);
}
time = DateUtil.delta(start);
System.out.println("StringDecoder");
System.out.println("time: " + time);
writer = new StringWriter();
d.decodeTo(writer, bytes, 0, bytes.length);
str = writer.toString();
System.out.println("str: \"" + str + "\"");
System.out.println("chars.length: " + str.length());
System.out.println();
if (NIO_AVAILABLE) {
d = new CharsetDecoder("UTF-8");
for (int i = 0; i < 10000; i++) {
d.decodeTo(sink, bytes, 0, bytes.length);
}
start = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
d.decodeTo(sink, bytes, 0, bytes.length);
}
time = DateUtil.delta(start);
System.out.println("CharsetDecoder");
System.out.println("time: " + time);
writer = new StringWriter();
d.decodeTo(writer, bytes, 0, bytes.length);
str = writer.toString();
System.out.println("str: \"" + str + "\"");
System.out.println("chars.length: " + str.length());
System.out.println();
}
OutputStream os = new WriterOutputStream(new PrintWriter(System.out), "UTF-8");
os.write(bytes);
os.flush();
System.out.println();
for (byte b : bytes) {
os.write(b & 0xff);
}
os.flush();
}
///////////////////////////////////////////////////////////////////////////
private static interface Decoder {
void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException;
}
private static final class CharsetDecoder implements Decoder {
final Charset mCharset;
CharsetDecoder(String pCharset) {
// Handle null-case, to get default charset
String charset = pCharset != null ? pCharset :
System.getProperty("file.encoding", "ISO-8859-1");
mCharset = Charset.forName(charset);
}
public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
CharBuffer cb = mCharset.decode(ByteBuffer.wrap(pBytes, pOffset, pLength));
pWriter.write(cb.array(), 0, cb.length());
}
}
private static final class StringDecoder implements Decoder {
final String mCharset;
StringDecoder(String pCharset) {
mCharset = pCharset;
}
public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
String str = mCharset == null ?
new String(pBytes, pOffset, pLength) :
new String(pBytes, pOffset, pLength, mCharset);
pWriter.write(str);
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.DateUtil;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
/**
* Wraps a {@code Writer} in an {@code OutputStream}.
* <p>
* <em>Instances of this class are not thread-safe.</em>
* </p>
* <p>
* <em>NOTE: This class is probably not the right way of solving your problem,
* however it might prove useful in JSPs etc.
* If possible, it's always better to use the {@code Writer}'s underlying
* {@code OutputStream}, or wrap it's native backing.
* </em>
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/WriterOutputStream.java#2 $
*/
public class WriterOutputStream extends OutputStream {
protected Writer writer;
final protected Decoder decoder;
final ByteArrayOutputStream bufferStream = new FastByteArrayOutputStream(1024);
private volatile boolean isFlushing = false; // Ugly but critical...
private static final boolean NIO_AVAILABLE = isNIOAvailable();
private static boolean isNIOAvailable() {
try {
Class.forName("java.nio.charset.Charset");
return true;
}
catch (Throwable t) {
// Ignore
}
return false;
}
public WriterOutputStream(final Writer pWriter, final String pCharset) {
writer = pWriter;
decoder = getDecoder(pCharset);
}
public WriterOutputStream(final Writer pWriter) {
this(pWriter, null);
}
private static Decoder getDecoder(final String pCharset) {
// NOTE: The CharsetDecoder is typically 10-20% faster than
// StringDecoder according to my tests
// StringEncoder is horribly slow on 1.2 systems, but there's no
// alternative...
if (NIO_AVAILABLE) {
return new CharsetDecoder(pCharset);
}
return new StringDecoder(pCharset);
}
@Override
public void close() throws IOException {
flush();
writer.close();
writer = null;
}
@Override
public void flush() throws IOException {
flushBuffer();
writer.flush();
}
@Override
public final void write(byte[] pBytes) throws IOException {
if (pBytes == null) {
throw new NullPointerException("bytes == null");
}
write(pBytes, 0, pBytes.length);
}
@Override
public final void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
flushBuffer();
decoder.decodeTo(writer, pBytes, pOffset, pLength);
}
@Override
public final void write(int pByte) {
// TODO: Is it possible to know if this is a good place in the stream to
// flush? It might be in the middle of a multi-byte encoded character..
bufferStream.write(pByte);
}
private void flushBuffer() throws IOException {
if (!isFlushing && bufferStream.size() > 0) {
isFlushing = true;
bufferStream.writeTo(this); // NOTE: Avoids cloning buffer array
bufferStream.reset();
isFlushing = false;
}
}
///////////////////////////////////////////////////////////////////////////
public static void main(String[] pArgs) throws IOException {
int iterations = 1000000;
byte[] bytes = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> klashf lkash ljah lhaaklhghdfgu ksd".getBytes("UTF-8");
Decoder d;
long start;
long time;
Writer sink = new PrintWriter(new NullOutputStream());
StringWriter writer;
String str;
d = new StringDecoder("UTF-8");
for (int i = 0; i < 10000; i++) {
d.decodeTo(sink, bytes, 0, bytes.length);
}
start = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
d.decodeTo(sink, bytes, 0, bytes.length);
}
time = DateUtil.delta(start);
System.out.println("StringDecoder");
System.out.println("time: " + time);
writer = new StringWriter();
d.decodeTo(writer, bytes, 0, bytes.length);
str = writer.toString();
System.out.println("str: \"" + str + "\"");
System.out.println("chars.length: " + str.length());
System.out.println();
if (NIO_AVAILABLE) {
d = new CharsetDecoder("UTF-8");
for (int i = 0; i < 10000; i++) {
d.decodeTo(sink, bytes, 0, bytes.length);
}
start = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
d.decodeTo(sink, bytes, 0, bytes.length);
}
time = DateUtil.delta(start);
System.out.println("CharsetDecoder");
System.out.println("time: " + time);
writer = new StringWriter();
d.decodeTo(writer, bytes, 0, bytes.length);
str = writer.toString();
System.out.println("str: \"" + str + "\"");
System.out.println("chars.length: " + str.length());
System.out.println();
}
OutputStream os = new WriterOutputStream(new PrintWriter(System.out), "UTF-8");
os.write(bytes);
os.flush();
System.out.println();
for (byte b : bytes) {
os.write(b & 0xff);
}
os.flush();
}
///////////////////////////////////////////////////////////////////////////
private static interface Decoder {
void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException;
}
private static final class CharsetDecoder implements Decoder {
final Charset mCharset;
CharsetDecoder(String pCharset) {
// Handle null-case, to get default charset
String charset = pCharset != null ? pCharset :
System.getProperty("file.encoding", "ISO-8859-1");
mCharset = Charset.forName(charset);
}
public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
CharBuffer cb = mCharset.decode(ByteBuffer.wrap(pBytes, pOffset, pLength));
pWriter.write(cb.array(), 0, cb.length());
}
}
private static final class StringDecoder implements Decoder {
final String mCharset;
StringDecoder(String pCharset) {
mCharset = pCharset;
}
public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
String str = mCharset == null ?
new String(pBytes, pOffset, pLength) :
new String(pBytes, pOffset, pLength, mCharset);
pWriter.write(str);
}
}
}

View File

@@ -1,188 +1,188 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
* {@code Decoder} implementation for standard base64 encoding.
* <p/>
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
* @see <a href="http://tools.ietf.org/html/rfc2045"RFC 2045</a>
*
* @see Base64Encoder
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Base64Decoder.java#2 $
*/
public final class Base64Decoder implements Decoder {
/**
* This array maps the characters to their 6 bit values
*/
final static byte[] PEM_ARRAY = {
//0 1 2 3 4 5 6 7
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
'4', '5', '6', '7', '8', '9', '+', '/' // 7
};
final static byte[] PEM_CONVERT_ARRAY;
private byte[] decodeBuffer = new byte[4];
static {
PEM_CONVERT_ARRAY = new byte[256];
for (int i = 0; i < 255; i++) {
PEM_CONVERT_ARRAY[i] = -1;
}
for (int i = 0; i < PEM_ARRAY.length; i++) {
PEM_CONVERT_ARRAY[PEM_ARRAY[i]] = (byte) i;
}
}
protected static int readFully(final InputStream pStream, final byte pBytes[], final int pOffset, final int pLength)
throws IOException
{
for (int i = 0; i < pLength; i++) {
int read = pStream.read();
if (read == -1) {
return i != 0 ? i : -1;
}
pBytes[i + pOffset] = (byte) read;
}
return pLength;
}
protected boolean decodeAtom(final InputStream pInput, final ByteBuffer pOutput, final int pLength)
throws IOException {
byte byte0 = -1;
byte byte1 = -1;
byte byte2 = -1;
byte byte3 = -1;
if (pLength < 2) {
throw new IOException("BASE64Decoder: Not enough bytes for an atom.");
}
int read;
// Skip line feeds
do {
read = pInput.read();
if (read == -1) {
return false;
}
} while (read == 10 || read == 13);
decodeBuffer[0] = (byte) read;
read = readFully(pInput, decodeBuffer, 1, pLength - 1);
if (read == -1) {
return false;
}
int length = pLength;
if (length > 3 && decodeBuffer[3] == 61) {
length = 3;
}
if (length > 2 && decodeBuffer[2] == 61) {
length = 2;
}
switch (length) {
case 4:
byte3 = PEM_CONVERT_ARRAY[decodeBuffer[3] & 255];
// fall through
case 3:
byte2 = PEM_CONVERT_ARRAY[decodeBuffer[2] & 255];
// fall through
case 2:
byte1 = PEM_CONVERT_ARRAY[decodeBuffer[1] & 255];
byte0 = PEM_CONVERT_ARRAY[decodeBuffer[0] & 255];
// fall through
default:
switch (length) {
case 2:
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
break;
case 3:
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
break;
case 4:
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
pOutput.put((byte) (byte2 << 6 & 192 | byte3 & 63));
break;
}
break;
}
return true;
}
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
do {
int k = 72;
int i;
for (i = 0; i + 4 < k; i += 4) {
if(!decodeAtom(stream, buffer, 4)) {
break;
}
}
if (!decodeAtom(stream, buffer, k - i)) {
break;
}
}
while (buffer.remaining() > 54); // 72 char lines should produce no more than 54 bytes
return buffer.position();
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
* {@code Decoder} implementation for standard base64 encoding.
*
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
* @see <a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a>
*
* @see Base64Encoder
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Base64Decoder.java#2 $
*/
public final class Base64Decoder implements Decoder {
/**
* This array maps the characters to their 6 bit values
*/
final static byte[] PEM_ARRAY = {
//0 1 2 3 4 5 6 7
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
'4', '5', '6', '7', '8', '9', '+', '/' // 7
};
final static byte[] PEM_CONVERT_ARRAY;
private byte[] decodeBuffer = new byte[4];
static {
PEM_CONVERT_ARRAY = new byte[256];
for (int i = 0; i < 255; i++) {
PEM_CONVERT_ARRAY[i] = -1;
}
for (int i = 0; i < PEM_ARRAY.length; i++) {
PEM_CONVERT_ARRAY[PEM_ARRAY[i]] = (byte) i;
}
}
protected static int readFully(final InputStream pStream, final byte pBytes[], final int pOffset, final int pLength)
throws IOException
{
for (int i = 0; i < pLength; i++) {
int read = pStream.read();
if (read == -1) {
return i != 0 ? i : -1;
}
pBytes[i + pOffset] = (byte) read;
}
return pLength;
}
protected boolean decodeAtom(final InputStream pInput, final ByteBuffer pOutput, final int pLength)
throws IOException {
byte byte0 = -1;
byte byte1 = -1;
byte byte2 = -1;
byte byte3 = -1;
if (pLength < 2) {
throw new IOException("BASE64Decoder: Not enough bytes for an atom.");
}
int read;
// Skip line feeds
do {
read = pInput.read();
if (read == -1) {
return false;
}
} while (read == 10 || read == 13);
decodeBuffer[0] = (byte) read;
read = readFully(pInput, decodeBuffer, 1, pLength - 1);
if (read == -1) {
return false;
}
int length = pLength;
if (length > 3 && decodeBuffer[3] == 61) {
length = 3;
}
if (length > 2 && decodeBuffer[2] == 61) {
length = 2;
}
switch (length) {
case 4:
byte3 = PEM_CONVERT_ARRAY[decodeBuffer[3] & 255];
// fall through
case 3:
byte2 = PEM_CONVERT_ARRAY[decodeBuffer[2] & 255];
// fall through
case 2:
byte1 = PEM_CONVERT_ARRAY[decodeBuffer[1] & 255];
byte0 = PEM_CONVERT_ARRAY[decodeBuffer[0] & 255];
// fall through
default:
switch (length) {
case 2:
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
break;
case 3:
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
break;
case 4:
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
pOutput.put((byte) (byte2 << 6 & 192 | byte3 & 63));
break;
}
break;
}
return true;
}
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
do {
int k = 72;
int i;
for (i = 0; i + 4 < k; i += 4) {
if(!decodeAtom(stream, buffer, 4)) {
break;
}
}
if (!decodeAtom(stream, buffer, k - i)) {
break;
}
}
while (buffer.remaining() > 54); // 72 char lines should produce no more than 54 bytes
return buffer.position();
}
}

View File

@@ -1,106 +1,106 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* {@code Encoder} implementation for standard base64 encoding.
* <p/>
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
* @see <a href="http://tools.ietf.org/html/rfc2045"RFC 2045</a>
*
* @see Base64Decoder
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Base64Encoder.java#2 $
*/
public class Base64Encoder implements Encoder {
public void encode(final OutputStream stream, final ByteBuffer buffer)
throws IOException
{
// TODO: Implement
// NOTE: This is impossible, given the current spec, as we need to either:
// - buffer all data in the EncoderStream
// - or have flush/end method(s) in the Encoder
// to ensure proper end of stream handling
int length;
// TODO: Temp impl, will only work for single writes
while (buffer.hasRemaining()) {
byte a, b, c;
// if ((buffer.remaining()) > 2) {
// length = 3;
// }
// else {
// length = buffer.remaining();
// }
length = Math.min(3, buffer.remaining());
switch (length) {
case 1:
a = buffer.get();
b = 0;
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
stream.write('=');
stream.write('=');
break;
case 2:
a = buffer.get();
b = buffer.get();
c = 0;
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
stream.write('=');
break;
default:
a = buffer.get();
b = buffer.get();
c = buffer.get();
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
stream.write(Base64Decoder.PEM_ARRAY[c & 0x3F]);
break;
}
}
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* {@code Encoder} implementation for standard base64 encoding.
*
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
* @see <a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a>
*
* @see Base64Decoder
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Base64Encoder.java#2 $
*/
public class Base64Encoder implements Encoder {
public void encode(final OutputStream stream, final ByteBuffer buffer)
throws IOException
{
// TODO: Implement
// NOTE: This is impossible, given the current spec, as we need to either:
// - buffer all data in the EncoderStream
// - or have flush/end method(s) in the Encoder
// to ensure proper end of stream handling
int length;
// TODO: Temp impl, will only work for single writes
while (buffer.hasRemaining()) {
byte a, b, c;
// if ((buffer.remaining()) > 2) {
// length = 3;
// }
// else {
// length = buffer.remaining();
// }
length = Math.min(3, buffer.remaining());
switch (length) {
case 1:
a = buffer.get();
b = 0;
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
stream.write('=');
stream.write('=');
break;
case 2:
a = buffer.get();
b = buffer.get();
c = 0;
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
stream.write('=');
break;
default:
a = buffer.get();
b = buffer.get();
c = buffer.get();
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
stream.write(Base64Decoder.PEM_ARRAY[c & 0x3F]);
break;
}
}
}
}

View File

@@ -1,56 +1,55 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.IOException;
/**
* Thrown by {@code Decoder}s when encoded data can not be decoded.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecodeException.java#2 $
*/
public class DecodeException extends IOException {
public DecodeException(final String pMessage) {
super(pMessage);
}
public DecodeException(final String pMessage, final Throwable pCause) {
super(pMessage);
initCause(pCause);
}
public DecodeException(final Throwable pCause) {
this(pCause.getMessage(), pCause);
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.IOException;
/**
* Thrown by {@code Decoder}s when encoded data can not be decoded.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecodeException.java#2 $
*/
public class DecodeException extends IOException {
public DecodeException(final String pMessage) {
super(pMessage);
}
public DecodeException(final String pMessage, final Throwable pCause) {
super(pMessage);
initCause(pCause);
}
public DecodeException(final Throwable pCause) {
this(pCause.getMessage(), pCause);
}
}

View File

@@ -1,68 +1,69 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
* Interface for decoders.
* A {@code Decoder} may be used with a {@code DecoderStream}, to perform
* on-the-fly decoding from an {@code InputStream}.
* <p/>
* Important note: Decoder implementations are typically not synchronized.
* <p/>
* @see Encoder
* @see DecoderStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Decoder.java#2 $
*/
public interface Decoder {
/**
* Decodes up to {@code buffer.length} bytes from the given input stream,
* into the given buffer.
*
* @param stream the input stream to decode data from
* @param buffer buffer to store the read data
*
* @return the total number of bytes read into the buffer, or {@code 0}
* if there is no more data because the end of the stream has been reached.
*
* @throws DecodeException if encoded data is corrupt.
* @throws IOException if an I/O error occurs.
* @throws java.io.EOFException if a premature end-of-file is encountered.
* @throws java.lang.NullPointerException if either argument is {@code null}.
*/
int decode(InputStream stream, ByteBuffer buffer) throws IOException;
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
* Interface for decoders.
* A {@code Decoder} may be used with a {@code DecoderStream}, to perform
* on-the-fly decoding from an {@code InputStream}.
* <p>
* Important note: Decoder implementations are typically not synchronized.
* </p>
*
* @see Encoder
* @see DecoderStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Decoder.java#2 $
*/
public interface Decoder {
/**
* Decodes up to {@code buffer.length} bytes from the given input stream,
* into the given buffer.
*
* @param stream the input stream to decode data from
* @param buffer buffer to store the read data
*
* @return the total number of bytes read into the buffer, or {@code 0}
* if there is no more data because the end of the stream has been reached.
*
* @throws DecodeException if encoded data is corrupt.
* @throws IOException if an I/O error occurs.
* @throws java.io.EOFException if a premature end-of-file is encountered.
* @throws java.lang.NullPointerException if either argument is {@code null}.
*/
int decode(InputStream stream, ByteBuffer buffer) throws IOException;
}

View File

@@ -1,200 +1,199 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
* An {@code InputStream} that provides on-the-fly decoding from an underlying
* stream.
* <p/>
* @see EncoderStream
* @see Decoder
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java#2 $
*/
public final class DecoderStream extends FilterInputStream {
protected final ByteBuffer buffer;
protected final Decoder decoder;
/**
* Creates a new decoder stream and chains it to the
* input stream specified by the {@code pStream} argument.
* The stream will use a default decode buffer size.
*
* @param pStream the underlying input stream.
* @param pDecoder the decoder that will be used to decode the underlying stream
*
* @see java.io.FilterInputStream#in
*/
public DecoderStream(final InputStream pStream, final Decoder pDecoder) {
// TODO: Let the decoder decide preferred buffer size
this(pStream, pDecoder, 1024);
}
/**
* Creates a new decoder stream and chains it to the
* input stream specified by the {@code pStream} argument.
*
* @param pStream the underlying input stream.
* @param pDecoder the decoder that will be used to decode the underlying stream
* @param pBufferSize the size of the decode buffer
*
* @see java.io.FilterInputStream#in
*/
public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
super(pStream);
decoder = pDecoder;
buffer = ByteBuffer.allocate(pBufferSize);
buffer.flip();
}
public int available() throws IOException {
return buffer.remaining();
}
public int read() throws IOException {
if (!buffer.hasRemaining()) {
if (fill() < 0) {
return -1;
}
}
return buffer.get() & 0xff;
}
public int read(final byte pBytes[], final int pOffset, final int pLength) throws IOException {
if (pBytes == null) {
throw new NullPointerException();
}
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
throw new IndexOutOfBoundsException("bytes.length=" + pBytes.length + " offset=" + pOffset + " length=" + pLength);
}
else if (pLength == 0) {
return 0;
}
// End of file?
if (!buffer.hasRemaining()) {
if (fill() < 0) {
return -1;
}
}
// Read until we have read pLength bytes, or have reached EOF
int count = 0;
int off = pOffset;
while (pLength > count) {
if (!buffer.hasRemaining()) {
if (fill() < 0) {
break;
}
}
// Copy as many bytes as possible
int dstLen = Math.min(pLength - count, buffer.remaining());
buffer.get(pBytes, off, dstLen);
// Update offset (rest)
off += dstLen;
// Increase count
count += dstLen;
}
return count;
}
public long skip(final long pLength) throws IOException {
// End of file?
if (!buffer.hasRemaining()) {
if (fill() < 0) {
return 0; // Yes, 0, not -1
}
}
// Skip until we have skipped pLength bytes, or have reached EOF
long total = 0;
while (total < pLength) {
if (!buffer.hasRemaining()) {
if (fill() < 0) {
break;
}
}
// NOTE: Skipped can never be more than avail, which is an int, so the cast is safe
int skipped = (int) Math.min(pLength - total, buffer.remaining());
buffer.position(buffer.position() + skipped);
total += skipped;
}
return total;
}
/**
* Fills the buffer, by decoding data from the underlying input stream.
*
* @return the number of bytes decoded, or {@code -1} if the end of the
* file is reached
*
* @throws IOException if an I/O error occurs
*/
protected int fill() throws IOException {
buffer.clear();
int read = decoder.decode(in, buffer);
// TODO: Enforce this in test case, leave here to aid debugging
if (read > buffer.capacity()) {
throw new AssertionError(
String.format(
"Decode beyond buffer (%d): %d (using %s decoder)",
buffer.capacity(), read, decoder.getClass().getName()
)
);
}
buffer.flip();
if (read == 0) {
return -1;
}
return read;
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
* An {@code InputStream} that provides on-the-fly decoding from an underlying stream.
*
* @see EncoderStream
* @see Decoder
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java#2 $
*/
public final class DecoderStream extends FilterInputStream {
protected final ByteBuffer buffer;
protected final Decoder decoder;
/**
* Creates a new decoder stream and chains it to the
* input stream specified by the {@code pStream} argument.
* The stream will use a default decode buffer size.
*
* @param pStream the underlying input stream.
* @param pDecoder the decoder that will be used to decode the underlying stream
*
* @see java.io.FilterInputStream#in
*/
public DecoderStream(final InputStream pStream, final Decoder pDecoder) {
// TODO: Let the decoder decide preferred buffer size
this(pStream, pDecoder, 1024);
}
/**
* Creates a new decoder stream and chains it to the
* input stream specified by the {@code pStream} argument.
*
* @param pStream the underlying input stream.
* @param pDecoder the decoder that will be used to decode the underlying stream
* @param pBufferSize the size of the decode buffer
*
* @see java.io.FilterInputStream#in
*/
public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
super(pStream);
decoder = pDecoder;
buffer = ByteBuffer.allocate(pBufferSize);
buffer.flip();
}
public int available() throws IOException {
return buffer.remaining();
}
public int read() throws IOException {
if (!buffer.hasRemaining()) {
if (fill() < 0) {
return -1;
}
}
return buffer.get() & 0xff;
}
public int read(final byte pBytes[], final int pOffset, final int pLength) throws IOException {
if (pBytes == null) {
throw new NullPointerException();
}
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
throw new IndexOutOfBoundsException("bytes.length=" + pBytes.length + " offset=" + pOffset + " length=" + pLength);
}
else if (pLength == 0) {
return 0;
}
// End of file?
if (!buffer.hasRemaining()) {
if (fill() < 0) {
return -1;
}
}
// Read until we have read pLength bytes, or have reached EOF
int count = 0;
int off = pOffset;
while (pLength > count) {
if (!buffer.hasRemaining()) {
if (fill() < 0) {
break;
}
}
// Copy as many bytes as possible
int dstLen = Math.min(pLength - count, buffer.remaining());
buffer.get(pBytes, off, dstLen);
// Update offset (rest)
off += dstLen;
// Increase count
count += dstLen;
}
return count;
}
public long skip(final long pLength) throws IOException {
// End of file?
if (!buffer.hasRemaining()) {
if (fill() < 0) {
return 0; // Yes, 0, not -1
}
}
// Skip until we have skipped pLength bytes, or have reached EOF
long total = 0;
while (total < pLength) {
if (!buffer.hasRemaining()) {
if (fill() < 0) {
break;
}
}
// NOTE: Skipped can never be more than avail, which is an int, so the cast is safe
int skipped = (int) Math.min(pLength - total, buffer.remaining());
buffer.position(buffer.position() + skipped);
total += skipped;
}
return total;
}
/**
* Fills the buffer, by decoding data from the underlying input stream.
*
* @return the number of bytes decoded, or {@code -1} if the end of the
* file is reached
*
* @throws IOException if an I/O error occurs
*/
protected int fill() throws IOException {
buffer.clear();
int read = decoder.decode(in, buffer);
// TODO: Enforce this in test case, leave here to aid debugging
if (read > buffer.capacity()) {
throw new AssertionError(
String.format(
"Decode beyond buffer (%d): %d (using %s decoder)",
buffer.capacity(), read, decoder.getClass().getName()
)
);
}
buffer.flip();
if (read == 0) {
return -1;
}
return read;
}
}

View File

@@ -1,65 +1,66 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* Interface for encoders.
* An {@code Encoder} may be used with an {@code EncoderStream}, to perform
* on-the-fly encoding to an {@code OutputStream}.
* <p/>
* Important note: Encoder implementations are typically not synchronized.
*
* @see Decoder
* @see EncoderStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Encoder.java#2 $
*/
public interface Encoder {
/**
* Encodes up to {@code buffer.remaining()} bytes into the given input stream,
* from the given buffer.
*
* @param stream the output stream to encode data to
* @param buffer buffer to read data from
*
* @throws java.io.IOException if an I/O error occurs
*/
void encode(OutputStream stream, ByteBuffer buffer) throws IOException;
//TODO: int requiredBufferSize(): -1 == any, otherwise, use this buffer size
// void flush()?
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* Interface for encoders.
* An {@code Encoder} may be used with an {@code EncoderStream}, to perform
* on-the-fly encoding to an {@code OutputStream}.
* <p>
* Important note: Encoder implementations are typically not synchronized.
* </p>
*
* @see Decoder
* @see EncoderStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Encoder.java#2 $
*/
public interface Encoder {
/**
* Encodes up to {@code buffer.remaining()} bytes into the given input stream,
* from the given buffer.
*
* @param stream the output stream to encode data to
* @param buffer buffer to read data from
*
* @throws java.io.IOException if an I/O error occurs
*/
void encode(OutputStream stream, ByteBuffer buffer) throws IOException;
//TODO: int requiredBufferSize(): -1 == any, otherwise, use this buffer size
// void flush()?
}

View File

@@ -1,137 +1,136 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* An {@code OutputStream} that provides on-the-fly encoding to an underlying
* stream.
* <p/>
* @see DecoderStream
* @see Encoder
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java#2 $
*/
public final class EncoderStream extends FilterOutputStream {
// TODO: This class need a test case ASAP!!!
protected final Encoder encoder;
private final boolean flushOnWrite;
protected final ByteBuffer buffer;
/**
* Creates an output stream filter built on top of the specified
* underlying output stream.
*
* @param pStream the underlying output stream
* @param pEncoder the encoder to use
*/
public EncoderStream(final OutputStream pStream, final Encoder pEncoder) {
this(pStream, pEncoder, false);
}
/**
* Creates an output stream filter built on top of the specified
* underlying output stream.
*
* @param pStream the underlying output stream
* @param pEncoder the encoder to use
* @param pFlushOnWrite if {@code true}, calls to the byte-array
* {@code write} methods will automatically flush the buffer.
*/
public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
super(pStream);
encoder = pEncoder;
flushOnWrite = pFlushOnWrite;
buffer = ByteBuffer.allocate(1024);
buffer.flip();
}
public void close() throws IOException {
flush();
super.close();
}
public void flush() throws IOException {
encodeBuffer();
super.flush();
}
private void encodeBuffer() throws IOException {
if (buffer.position() != 0) {
buffer.flip();
// Make sure all remaining data in buffer is written to the stream
encoder.encode(out, buffer);
// Reset buffer
buffer.clear();
}
}
public final void write(final byte[] pBytes) throws IOException {
write(pBytes, 0, pBytes.length);
}
// TODO: Verify that this works for the general case (it probably won't)...
// TODO: We might need a way to explicitly flush the encoder, or specify
// that the encoder can't buffer. In that case, the encoder should probably
// tell the EncoderStream how large buffer it prefers...
public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (!flushOnWrite && pLength < buffer.remaining()) {
// Buffer data
buffer.put(pBytes, pOffset, pLength);
}
else {
// Encode data already in the buffer
encodeBuffer();
// Encode rest without buffering
encoder.encode(out, ByteBuffer.wrap(pBytes, pOffset, pLength));
}
}
public void write(final int pByte) throws IOException {
if (!buffer.hasRemaining()) {
encodeBuffer(); // Resets bufferPos to 0
}
buffer.put((byte) pByte);
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* An {@code OutputStream} that provides on-the-fly encoding to an underlying stream.
*
* @see DecoderStream
* @see Encoder
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java#2 $
*/
public final class EncoderStream extends FilterOutputStream {
// TODO: This class need a test case ASAP!!!
protected final Encoder encoder;
private final boolean flushOnWrite;
protected final ByteBuffer buffer;
/**
* Creates an output stream filter built on top of the specified
* underlying output stream.
*
* @param pStream the underlying output stream
* @param pEncoder the encoder to use
*/
public EncoderStream(final OutputStream pStream, final Encoder pEncoder) {
this(pStream, pEncoder, false);
}
/**
* Creates an output stream filter built on top of the specified
* underlying output stream.
*
* @param pStream the underlying output stream
* @param pEncoder the encoder to use
* @param pFlushOnWrite if {@code true}, calls to the byte-array
* {@code write} methods will automatically flush the buffer.
*/
public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
super(pStream);
encoder = pEncoder;
flushOnWrite = pFlushOnWrite;
buffer = ByteBuffer.allocate(1024);
buffer.flip();
}
public void close() throws IOException {
flush();
super.close();
}
public void flush() throws IOException {
encodeBuffer();
super.flush();
}
private void encodeBuffer() throws IOException {
if (buffer.position() != 0) {
buffer.flip();
// Make sure all remaining data in buffer is written to the stream
encoder.encode(out, buffer);
// Reset buffer
buffer.clear();
}
}
public final void write(final byte[] pBytes) throws IOException {
write(pBytes, 0, pBytes.length);
}
// TODO: Verify that this works for the general case (it probably won't)...
// TODO: We might need a way to explicitly flush the encoder, or specify
// that the encoder can't buffer. In that case, the encoder should probably
// tell the EncoderStream how large buffer it prefers...
public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (!flushOnWrite && pLength < buffer.remaining()) {
// Buffer data
buffer.put(pBytes, pOffset, pLength);
}
else {
// Encode data already in the buffer
encodeBuffer();
// Encode rest without buffering
encoder.encode(out, ByteBuffer.wrap(pBytes, pOffset, pLength));
}
}
public void write(final int pByte) throws IOException {
if (!buffer.hasRemaining()) {
encodeBuffer(); // Resets bufferPos to 0
}
buffer.put((byte) pByte);
}
}

View File

@@ -37,30 +37,36 @@ import java.nio.ByteBuffer;
/**
* Decoder implementation for Apple PackBits run-length encoding.
* <p/>
* <small>From Wikipedia, the free encyclopedia</small><br/>
* <p>
* <small>From Wikipedia, the free encyclopedia</small>
* <br>
* PackBits is a fast, simple compression scheme for run-length encoding of
* data.
* <p/>
* </p>
* <p>
* Apple introduced the PackBits format with the release of MacPaint on the
* Macintosh computer. This compression scheme is one of the types of
* compression that can be used in TIFF-files.
* <p/>
* </p>
* <p>
* A PackBits data stream consists of packets of one byte of header followed by
* data. The header is a signed byte; the data can be signed, unsigned, or
* packed (such as MacPaint pixels).
* <p/>
* <table><tr><th>Header byte</th><th>Data</th></tr>
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in
* the decompressed output</td></tr>
* <tr><td>-128</td> <td>No operation</td></tr></table>
* <p/>
* </p>
* <table>
* <caption>PackBits</caption>
* <tr><th>Header byte</th><th>Data</th></tr>
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in the decompressed output</td></tr>
* <tr><td>-128</td> <td>No operation</td></tr>
* </table>
* <p>
* Note that interpreting 0 as positive or negative makes no difference in the
* output. Runs of two bytes adjacent to non-runs are typically written as
* literal data.
* <p/>
* See <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
* </p>
*
* @see <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java#1 $
@@ -80,10 +86,11 @@ public final class PackBitsDecoder implements Decoder {
/**
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
* <p/>
* <p>
* As some implementations of PackBits-like encoders treat {@code -128} as length of
* a compressed run, instead of a no-op, it's possible to disable no-ops for compatibility.
* Should be used with caution, even though, most known encoders never write no-ops in the compressed streams.
* </p>
*
* @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
*/
@@ -93,10 +100,11 @@ public final class PackBitsDecoder implements Decoder {
/**
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
* <p/>
* <p>
* As some implementations of PackBits-like encoders treat {@code -128} as length of
* a compressed run, instead of a no-op, it's possible to disable no-ops for compatibility.
* Should be used with caution, even though, most known encoders never write no-ops in the compressed streams.
* </p>
*
* @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
*/

View File

@@ -1,132 +1,138 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* Encoder implementation for Apple PackBits run-length encoding.
* <p/>
* From Wikipedia, the free encyclopedia<br/>
* PackBits is a fast, simple compression scheme for run-length encoding of
* data.
* <p/>
* Apple introduced the PackBits format with the release of MacPaint on the
* Macintosh computer. This compression scheme is one of the types of
* compression that can be used in TIFF-files.
* <p/>
* A PackBits data stream consists of packets of one byte of header followed by
* data. The header is a signed byte; the data can be signed, unsigned, or
* packed (such as MacPaint pixels).
* <p/>
* <table><tr><th>Header byte</th><th>Data</th></tr>
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in
* the decompressed output</td></tr>
* <tr><td>-128</td> <td>No operation</td></tr></table>
* <p/>
* Note that interpreting 0 as positive or negative makes no difference in the
* output. Runs of two bytes adjacent to non-runs are typically written as
* literal data.
* <p/>
* See <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsEncoder.java#1 $
*/
public final class PackBitsEncoder implements Encoder {
final private byte[] buffer = new byte[128];
/**
* Creates a {@code PackBitsEncoder}.
*/
public PackBitsEncoder() {
}
public void encode(final OutputStream stream, final ByteBuffer buffer) throws IOException {
encode(stream, buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
buffer.position(buffer.remaining());
}
private void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException {
// NOTE: It's best to encode a 2 byte repeat
// run as a replicate run except when preceded and followed by a
// literal run, in which case it's best to merge the three into one
// literal run. Always encode 3 byte repeats as replicate runs.
// NOTE: Worst case: output = input + (input + 127) / 128
int offset = pOffset;
final int max = pOffset + pLength - 1;
final int maxMinus1 = max - 1;
while (offset <= max) {
// Compressed run
int run = 1;
byte replicate = pBuffer[offset];
while (run < 127 && offset < max && pBuffer[offset] == pBuffer[offset + 1]) {
offset++;
run++;
}
if (run > 1) {
offset++;
pStream.write(-(run - 1));
pStream.write(replicate);
}
// Literal run
run = 0;
while ((run < 128 && ((offset < max && pBuffer[offset] != pBuffer[offset + 1])
|| (offset < maxMinus1 && pBuffer[offset] != pBuffer[offset + 2])))) {
buffer[run++] = pBuffer[offset++];
}
// If last byte, include it in literal run, if space
if (offset == max && run > 0 && run < 128) {
buffer[run++] = pBuffer[offset++];
}
if (run > 0) {
pStream.write(run - 1);
pStream.write(buffer, 0, run);
}
// If last byte, and not space, start new literal run
if (offset == max && (run <= 0 || run >= 128)) {
pStream.write(0);
pStream.write(pBuffer[offset++]);
}
}
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.enc;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* Encoder implementation for Apple PackBits run-length encoding.
* <p>
* From Wikipedia, the free encyclopedia
* <br>
* PackBits is a fast, simple compression scheme for run-length encoding of
* data.
* </p>
* <p>
* Apple introduced the PackBits format with the release of MacPaint on the
* Macintosh computer. This compression scheme is one of the types of
* compression that can be used in TIFF-files.
* </p>
* <p>
* A PackBits data stream consists of packets of one byte of header followed by
* data. The header is a signed byte; the data can be signed, unsigned, or
* packed (such as MacPaint pixels).
* </p>
* <table>
* <caption>PackBits</caption>
* <tr><th>Header byte</th><th>Data</th></tr>
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in the decompressed output</td></tr>
* <tr><td>-128</td> <td>No operation</td></tr>
* </table>
* <p>
* Note that interpreting 0 as positive or negative makes no difference in the
* output. Runs of two bytes adjacent to non-runs are typically written as
* literal data.
* </p>
*
* @see <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsEncoder.java#1 $
*/
public final class PackBitsEncoder implements Encoder {
final private byte[] buffer = new byte[128];
/**
* Creates a {@code PackBitsEncoder}.
*/
public PackBitsEncoder() {
}
public void encode(final OutputStream stream, final ByteBuffer buffer) throws IOException {
encode(stream, buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
buffer.position(buffer.remaining());
}
private void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException {
// NOTE: It's best to encode a 2 byte repeat
// run as a replicate run except when preceded and followed by a
// literal run, in which case it's best to merge the three into one
// literal run. Always encode 3 byte repeats as replicate runs.
// NOTE: Worst case: output = input + (input + 127) / 128
int offset = pOffset;
final int max = pOffset + pLength - 1;
final int maxMinus1 = max - 1;
while (offset <= max) {
// Compressed run
int run = 1;
byte replicate = pBuffer[offset];
while (run < 127 && offset < max && pBuffer[offset] == pBuffer[offset + 1]) {
offset++;
run++;
}
if (run > 1) {
offset++;
pStream.write(-(run - 1));
pStream.write(replicate);
}
// Literal run
run = 0;
while ((run < 128 && ((offset < max && pBuffer[offset] != pBuffer[offset + 1])
|| (offset < maxMinus1 && pBuffer[offset] != pBuffer[offset + 2])))) {
buffer[run++] = pBuffer[offset++];
}
// If last byte, include it in literal run, if space
if (offset == max && run > 0 && run < 128) {
buffer[run++] = pBuffer[offset++];
}
if (run > 0) {
pStream.write(run - 1);
pStream.write(buffer, 0, run);
}
// If last byte, and not space, start new literal run
if (offset == max && (run <= 0 || run >= 128)) {
pStream.write(0);
pStream.write(pBuffer[offset++]);
}
}
}
}

View File

@@ -1,342 +1,344 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.ole2;
import com.twelvemonkeys.io.SeekableInputStream;
import java.io.DataInput;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Represents an OLE 2 compound document entry.
* This is similar to a file in a file system, or an entry in a ZIP or JAR file.
*
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/ole2/Entry.java#4 $
* @see com.twelvemonkeys.io.ole2.CompoundDocument
*/
// TODO: Consider extending java.io.File...
public final class Entry implements Comparable<Entry> {
String name;
byte type;
byte nodeColor;
int prevDId;
int nextDId;
int rootNodeDId;
long createdTimestamp;
long modifiedTimestamp;
int startSId;
int streamSize;
CompoundDocument document;
Entry parent;
SortedSet<Entry> children;
public final static int LENGTH = 128;
static final int EMPTY = 0;
static final int USER_STORAGE = 1;
static final int USER_STREAM = 2;
static final int LOCK_BYTES = 3;
static final int PROPERTY = 4;
static final int ROOT_STORAGE = 5;
private static final SortedSet<Entry> NO_CHILDREN = Collections.unmodifiableSortedSet(new TreeSet<Entry>());
private Entry() {
}
/**
* Reads an entry from the input.
*
* @param pInput the input data
* @return the {@code Entry} read from the input data
* @throws IOException if an i/o exception occurs during reading
*/
static Entry readEntry(final DataInput pInput) throws IOException {
Entry p = new Entry();
p.read(pInput);
return p;
}
/**
* Reads this entry
*
* @param pInput the input data
* @throws IOException if an i/o exception occurs during reading
*/
private void read(final DataInput pInput) throws IOException {
byte[] bytes = new byte[64];
pInput.readFully(bytes);
// NOTE: Length is in bytes, including the null-terminator...
int nameLength = pInput.readShort();
name = new String(bytes, 0, nameLength - 2, Charset.forName("UTF-16LE"));
// System.out.println("name: " + name);
type = pInput.readByte();
// System.out.println("type: " + type);
nodeColor = pInput.readByte();
// System.out.println("nodeColor: " + nodeColor);
prevDId = pInput.readInt();
// System.out.println("prevDId: " + prevDId);
nextDId = pInput.readInt();
// System.out.println("nextDId: " + nextDId);
rootNodeDId = pInput.readInt();
// System.out.println("rootNodeDId: " + rootNodeDId);
// UID (16) + user flags (4), ignored
if (pInput.skipBytes(20) != 20) {
throw new CorruptDocumentException();
}
createdTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
modifiedTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
startSId = pInput.readInt();
// System.out.println("startSId: " + startSId);
streamSize = pInput.readInt();
// System.out.println("streamSize: " + streamSize);
// Reserved
pInput.readInt();
}
/**
* If {@code true} this {@code Entry} is the root {@code Entry}.
*
* @return {@code true} if this is the root {@code Entry}
*/
public boolean isRoot() {
return type == ROOT_STORAGE;
}
/**
* If {@code true} this {@code Entry} is a directory
* {@code Entry}.
*
* @return {@code true} if this is a directory {@code Entry}
*/
public boolean isDirectory() {
return type == USER_STORAGE;
}
/**
* If {@code true} this {@code Entry} is a file (document)
* {@code Entry}.
*
* @return {@code true} if this is a document {@code Entry}
*/
public boolean isFile() {
return type == USER_STREAM;
}
/**
* Returns the name of this {@code Entry}
*
* @return the name of this {@code Entry}
*/
public String getName() {
return name;
}
/**
* Returns the {@code InputStream} for this {@code Entry}
*
* @return an {@code InputStream} containing the data for this
* {@code Entry} or {@code null} if this is a directory {@code Entry}
* @throws java.io.IOException if an I/O exception occurs
* @see #length()
*/
public SeekableInputStream getInputStream() throws IOException {
if (!isFile()) {
return null;
}
return document.getInputStreamForSId(startSId, streamSize);
}
/**
* Returns the length of this entry
*
* @return the length of the stream for this entry, or {@code 0} if this is
* a directory {@code Entry}
* @see #getInputStream()
*/
public long length() {
if (!isFile()) {
return 0L;
}
return streamSize;
}
/**
* Returns the time that this entry was created.
* The time is converted from its internal representation to standard Java
* representation, milliseconds since the epoch
* (00:00:00 GMT, January 1, 1970).
* <p/>
* Note that most applications leaves this value empty ({@code 0L}).
*
* @return A {@code long} value representing the time this entry was
* created, measured in milliseconds since the epoch
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
* creation time stamp exists for this entry.
*/
public long created() {
return createdTimestamp;
}
/**
* Returns the time that this entry was last modified.
* The time is converted from its internal representation to standard Java
* representation, milliseconds since the epoch
* (00:00:00 GMT, January 1, 1970).
* <p/>
* Note that many applications leaves this value empty ({@code 0L}).
*
* @return A {@code long} value representing the time this entry was
* last modified, measured in milliseconds since the epoch
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
* modification time stamp exists for this entry.
*/
public long lastModified() {
return modifiedTimestamp;
}
/**
* Return the parent of this {@code Entry}
*
* @return the parent of this {@code Entry}, or {@code null} if this is
* the root {@code Entry}
*/
public Entry getParentEntry() {
return parent;
}
/**
* Returns the child of this {@code Entry} with the given name.
*
* @param pName the name of the child {@code Entry}
* @return the child {@code Entry} or {@code null} if thee is no such
* child
* @throws java.io.IOException if an I/O exception occurs
*/
public Entry getChildEntry(final String pName) throws IOException {
if (isFile() || rootNodeDId == -1) {
return null;
}
Entry dummy = new Entry();
dummy.name = pName;
dummy.parent = this;
SortedSet child = getChildEntries().tailSet(dummy);
return (Entry) child.first();
}
/**
* Returns the children of this {@code Entry}.
*
* @return a {@code SortedSet} of {@code Entry} objects
* @throws java.io.IOException if an I/O exception occurs
*/
public SortedSet<Entry> getChildEntries() throws IOException {
if (children == null) {
if (isFile() || rootNodeDId == -1) {
children = NO_CHILDREN;
}
else {
// Start at root node in R/B tree, and read to the left and right,
// re-build tree, according to the docs
children = Collections.unmodifiableSortedSet(document.getEntries(rootNodeDId, this));
}
}
return children;
}
@Override
public String toString() {
return "\"" + name + "\""
+ " (" + (isFile() ? "Document" : (isDirectory() ? "Directory" : "Root"))
+ (parent != null ? ", parent: \"" + parent.getName() + "\"" : "")
+ (isFile() ? "" : ", children: " + (children != null ? String.valueOf(children.size()) : "(unknown)"))
+ ", SId=" + startSId + ", length=" + streamSize + ")";
}
@Override
public boolean equals(final Object pOther) {
if (pOther == this) {
return true;
}
if (!(pOther instanceof Entry)) {
return false;
}
Entry other = (Entry) pOther;
return name.equals(other.name) && (parent == other.parent
|| (parent != null && parent.equals(other.parent)));
}
@Override
public int hashCode() {
return name.hashCode() ^ startSId;
}
public int compareTo(final Entry pOther) {
if (this == pOther) {
return 0;
}
// NOTE: This is the sorting algorthm defined by the Compound Document:
// - first sort by name length
// - if lengths are equal, sort by comparing strings, case sensitive
int diff = name.length() - pOther.name.length();
if (diff != 0) {
return diff;
}
return name.compareTo(pOther.name);
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io.ole2;
import com.twelvemonkeys.io.SeekableInputStream;
import java.io.DataInput;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Represents an OLE 2 compound document entry.
* This is similar to a file in a file system, or an entry in a ZIP or JAR file.
*
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/ole2/Entry.java#4 $
* @see com.twelvemonkeys.io.ole2.CompoundDocument
*/
// TODO: Consider extending java.io.File...
public final class Entry implements Comparable<Entry> {
String name;
byte type;
byte nodeColor;
int prevDId;
int nextDId;
int rootNodeDId;
long createdTimestamp;
long modifiedTimestamp;
int startSId;
int streamSize;
CompoundDocument document;
Entry parent;
SortedSet<Entry> children;
public final static int LENGTH = 128;
static final int EMPTY = 0;
static final int USER_STORAGE = 1;
static final int USER_STREAM = 2;
static final int LOCK_BYTES = 3;
static final int PROPERTY = 4;
static final int ROOT_STORAGE = 5;
private static final SortedSet<Entry> NO_CHILDREN = Collections.unmodifiableSortedSet(new TreeSet<Entry>());
private Entry() {
}
/**
* Reads an entry from the input.
*
* @param pInput the input data
* @return the {@code Entry} read from the input data
* @throws IOException if an i/o exception occurs during reading
*/
static Entry readEntry(final DataInput pInput) throws IOException {
Entry p = new Entry();
p.read(pInput);
return p;
}
/**
* Reads this entry
*
* @param pInput the input data
* @throws IOException if an i/o exception occurs during reading
*/
private void read(final DataInput pInput) throws IOException {
byte[] bytes = new byte[64];
pInput.readFully(bytes);
// NOTE: Length is in bytes, including the null-terminator...
int nameLength = pInput.readShort();
name = new String(bytes, 0, nameLength - 2, Charset.forName("UTF-16LE"));
// System.out.println("name: " + name);
type = pInput.readByte();
// System.out.println("type: " + type);
nodeColor = pInput.readByte();
// System.out.println("nodeColor: " + nodeColor);
prevDId = pInput.readInt();
// System.out.println("prevDId: " + prevDId);
nextDId = pInput.readInt();
// System.out.println("nextDId: " + nextDId);
rootNodeDId = pInput.readInt();
// System.out.println("rootNodeDId: " + rootNodeDId);
// UID (16) + user flags (4), ignored
if (pInput.skipBytes(20) != 20) {
throw new CorruptDocumentException();
}
createdTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
modifiedTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
startSId = pInput.readInt();
// System.out.println("startSId: " + startSId);
streamSize = pInput.readInt();
// System.out.println("streamSize: " + streamSize);
// Reserved
pInput.readInt();
}
/**
* If {@code true} this {@code Entry} is the root {@code Entry}.
*
* @return {@code true} if this is the root {@code Entry}
*/
public boolean isRoot() {
return type == ROOT_STORAGE;
}
/**
* If {@code true} this {@code Entry} is a directory
* {@code Entry}.
*
* @return {@code true} if this is a directory {@code Entry}
*/
public boolean isDirectory() {
return type == USER_STORAGE;
}
/**
* If {@code true} this {@code Entry} is a file (document)
* {@code Entry}.
*
* @return {@code true} if this is a document {@code Entry}
*/
public boolean isFile() {
return type == USER_STREAM;
}
/**
* Returns the name of this {@code Entry}
*
* @return the name of this {@code Entry}
*/
public String getName() {
return name;
}
/**
* Returns the {@code InputStream} for this {@code Entry}
*
* @return an {@code InputStream} containing the data for this
* {@code Entry} or {@code null} if this is a directory {@code Entry}
* @throws java.io.IOException if an I/O exception occurs
* @see #length()
*/
public SeekableInputStream getInputStream() throws IOException {
if (!isFile()) {
return null;
}
return document.getInputStreamForSId(startSId, streamSize);
}
/**
* Returns the length of this entry
*
* @return the length of the stream for this entry, or {@code 0} if this is
* a directory {@code Entry}
* @see #getInputStream()
*/
public long length() {
if (!isFile()) {
return 0L;
}
return streamSize;
}
/**
* Returns the time that this entry was created.
* The time is converted from its internal representation to standard Java
* representation, milliseconds since the epoch
* (00:00:00 GMT, January 1, 1970).
* <p>
* Note that most applications leaves this value empty ({@code 0L}).
* </p>
*
* @return A {@code long} value representing the time this entry was
* created, measured in milliseconds since the epoch
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
* creation time stamp exists for this entry.
*/
public long created() {
return createdTimestamp;
}
/**
* Returns the time that this entry was last modified.
* The time is converted from its internal representation to standard Java
* representation, milliseconds since the epoch
* (00:00:00 GMT, January 1, 1970).
* <p>
* Note that many applications leaves this value empty ({@code 0L}).
* </p>
*
* @return A {@code long} value representing the time this entry was
* last modified, measured in milliseconds since the epoch
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
* modification time stamp exists for this entry.
*/
public long lastModified() {
return modifiedTimestamp;
}
/**
* Return the parent of this {@code Entry}
*
* @return the parent of this {@code Entry}, or {@code null} if this is
* the root {@code Entry}
*/
public Entry getParentEntry() {
return parent;
}
/**
* Returns the child of this {@code Entry} with the given name.
*
* @param pName the name of the child {@code Entry}
* @return the child {@code Entry} or {@code null} if thee is no such
* child
* @throws java.io.IOException if an I/O exception occurs
*/
public Entry getChildEntry(final String pName) throws IOException {
if (isFile() || rootNodeDId == -1) {
return null;
}
Entry dummy = new Entry();
dummy.name = pName;
dummy.parent = this;
SortedSet child = getChildEntries().tailSet(dummy);
return (Entry) child.first();
}
/**
* Returns the children of this {@code Entry}.
*
* @return a {@code SortedSet} of {@code Entry} objects
* @throws java.io.IOException if an I/O exception occurs
*/
public SortedSet<Entry> getChildEntries() throws IOException {
if (children == null) {
if (isFile() || rootNodeDId == -1) {
children = NO_CHILDREN;
}
else {
// Start at root node in R/B tree, and read to the left and right,
// re-build tree, according to the docs
children = Collections.unmodifiableSortedSet(document.getEntries(rootNodeDId, this));
}
}
return children;
}
@Override
public String toString() {
return "\"" + name + "\""
+ " (" + (isFile() ? "Document" : (isDirectory() ? "Directory" : "Root"))
+ (parent != null ? ", parent: \"" + parent.getName() + "\"" : "")
+ (isFile() ? "" : ", children: " + (children != null ? String.valueOf(children.size()) : "(unknown)"))
+ ", SId=" + startSId + ", length=" + streamSize + ")";
}
@Override
public boolean equals(final Object pOther) {
if (pOther == this) {
return true;
}
if (!(pOther instanceof Entry)) {
return false;
}
Entry other = (Entry) pOther;
return name.equals(other.name) && (parent == other.parent
|| (parent != null && parent.equals(other.parent)));
}
@Override
public int hashCode() {
return name.hashCode() ^ startSId;
}
public int compareTo(final Entry pOther) {
if (this == pOther) {
return 0;
}
// NOTE: This is the sorting algorthm defined by the Compound Document:
// - first sort by name length
// - if lengths are equal, sort by comparing strings, case sensitive
int diff = name.length() - pOther.name.length();
if (diff != 0) {
return diff;
}
return name.compareTo(pOther.name);
}
}

View File

@@ -1,314 +1,313 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.net;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.lang.SystemUtil;
import java.io.IOException;
import java.util.*;
/**
* Contains mappings from file extension to mime-types and from mime-type to file-types.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/net/MIMEUtil.java#5 $
*
* @see <A href="http://www.iana.org/assignments/media-types/">MIME Media Types</A>
*/
public final class MIMEUtil {
// TODO: Piggy-back on the mappings form the JRE? (1.6 comes with javax.activation)
// TODO: Piggy-back on mappings from javax.activation?
// See: http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/activation/MimetypesFileTypeMap.html
// See: http://java.sun.com/javase/6/docs/api/javax/activation/MimetypesFileTypeMap.html
// TODO: Use the format (and lookup) specified by the above URLs
// TODO: Allow 3rd party to add mappings? Will need application context support to do it safe.. :-P
private static Map<String, List<String>> sExtToMIME = new HashMap<String, List<String>>();
private static Map<String, List<String>> sUnmodifiableExtToMIME = Collections.unmodifiableMap(sExtToMIME);
private static Map<String, List<String>> sMIMEToExt = new HashMap<String, List<String>>();
private static Map<String, List<String>> sUnmodifiableMIMEToExt = Collections.unmodifiableMap(sMIMEToExt);
static {
// Load mapping for MIMEUtil
try {
Properties mappings = SystemUtil.loadProperties(MIMEUtil.class);
for (Map.Entry entry : mappings.entrySet()) {
// Convert and break up extensions and mimeTypes
String extStr = StringUtil.toLowerCase((String) entry.getKey());
List<String> extensions =
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(extStr, ";, ")));
String typeStr = StringUtil.toLowerCase((String) entry.getValue());
List<String> mimeTypes =
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(typeStr, ";, ")));
// TODO: Handle duplicates in MIME to extension mapping, like
// xhtml=application/xhtml+xml;application/xml
// xml=text/xml;application/xml
// Populate normal and reverse MIME-mappings
for (String extension : extensions) {
sExtToMIME.put(extension, mimeTypes);
}
for (String mimeType : mimeTypes) {
sMIMEToExt.put(mimeType, extensions);
}
}
}
catch (IOException e) {
System.err.println("Could not read properties for MIMEUtil: " + e.getMessage());
e.printStackTrace();
}
}
// Disallow construction
private MIMEUtil() {
}
/**
* Returns the default MIME type for the given file extension.
*
* @param pFileExt the file extension
*
* @return a {@code String} containing the MIME type, or {@code null} if
* there are no known MIME types for the given file extension.
*/
public static String getMIMEType(final String pFileExt) {
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
return (types == null || types.isEmpty()) ? null : types.get(0);
}
/**
* Returns all MIME types for the given file extension.
*
* @param pFileExt the file extension
*
* @return a {@link List} of {@code String}s containing the MIME types, or an empty
* list, if there are no known MIME types for the given file extension.
*/
public static List<String> getMIMETypes(final String pFileExt) {
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
return maskNull(types);
}
/**
* Returns an unmodifiabale {@link Map} view of the extension to
* MIME mapping, to use as the default mapping in client applications.
*
* @return an unmodifiabale {@code Map} view of the extension to
* MIME mapping.
*/
public static Map<String, List<String>> getMIMETypeMappings() {
return sUnmodifiableExtToMIME;
}
/**
* Returns the default file extension for the given MIME type.
* Specifying a wildcard type will return {@code null}.
*
* @param pMIME the MIME type
*
* @return a {@code String} containing the file extension, or {@code null}
* if there are no known file extensions for the given MIME type.
*/
public static String getExtension(final String pMIME) {
String mime = bareMIME(StringUtil.toLowerCase(pMIME));
List<String> extensions = sMIMEToExt.get(mime);
return (extensions == null || extensions.isEmpty()) ? null : extensions.get(0);
}
/**
* Returns all file extension for the given MIME type.
* The default extension will be the first in the list.
* Note that no specific order is given for wildcard types (image/*, *&#47;* etc).
*
* @param pMIME the MIME type
*
* @return a {@link List} of {@code String}s containing the MIME types, or an empty
* list, if there are no known file extensions for the given MIME type.
*/
public static List<String> getExtensions(final String pMIME) {
String mime = bareMIME(StringUtil.toLowerCase(pMIME));
if (mime.endsWith("/*")) {
return getExtensionForWildcard(mime);
}
List<String> extensions = sMIMEToExt.get(mime);
return maskNull(extensions);
}
// Gets all extensions for a wildcard MIME type
private static List<String> getExtensionForWildcard(final String pMIME) {
final String family = pMIME.substring(0, pMIME.length() - 1);
Set<String> extensions = new LinkedHashSet<String>();
for (Map.Entry<String, List<String>> mimeToExt : sMIMEToExt.entrySet()) {
if ("*/".equals(family) || mimeToExt.getKey().startsWith(family)) {
extensions.addAll(mimeToExt.getValue());
}
}
return Collections.unmodifiableList(new ArrayList<String>(extensions));
}
/**
* Returns an unmodifiabale {@link Map} view of the MIME to
* extension mapping, to use as the default mapping in client applications.
*
* @return an unmodifiabale {@code Map} view of the MIME to
* extension mapping.
*/
public static Map<String, List<String>> getExtensionMappings() {
return sUnmodifiableMIMEToExt;
}
/**
* Tests wehter the type is a subtype of the type family.
*
* @param pTypeFamily the MIME type family ({@code image/*, *&#47;*}, etc)
* @param pType the MIME type
* @return {@code true} if {@code pType} is a subtype of {@code pTypeFamily}, otherwise {@code false}
*/
// TODO: Rename? isSubtype?
// TODO: Make public
static boolean includes(final String pTypeFamily, final String pType) {
// TODO: Handle null in a well-defined way
// - Is null family same as */*?
// - Is null subtype of any family? Subtype of no family?
String type = bareMIME(pType);
return type.equals(pTypeFamily)
|| "*/*".equals(pTypeFamily)
|| pTypeFamily.endsWith("/*") && pTypeFamily.startsWith(type.substring(0, type.indexOf('/')));
}
/**
* Removes any charset or extra info from the mime-type string (anything after a semicolon, {@code ;}, inclusive).
*
* @param pMIME the mime-type string
* @return the bare mime-type
*/
public static String bareMIME(final String pMIME) {
int idx;
if (pMIME != null && (idx = pMIME.indexOf(';')) >= 0) {
return pMIME.substring(0, idx);
}
return pMIME;
}
// Returns the list or empty list if list is null
private static List<String> maskNull(List<String> pTypes) {
return (pTypes == null) ? Collections.<String>emptyList() : pTypes;
}
/**
* For debugging. Prints all known MIME types and file extensions.
*
* @param pArgs command line arguments
*/
public static void main(String[] pArgs) {
if (pArgs.length > 1) {
String type = pArgs[0];
String family = pArgs[1];
boolean incuded = includes(family, type);
System.out.println(
"Mime type family " + family
+ (incuded ? " includes " : " does not include ")
+ "type " + type
);
}
if (pArgs.length > 0) {
String str = pArgs[0];
if (str.indexOf('/') >= 0) {
// MIME
String extension = getExtension(str);
System.out.println("Default extension for MIME type '" + str + "' is "
+ (extension != null ? ": '" + extension + "'" : "unknown") + ".");
System.out.println("All possible: " + getExtensions(str));
}
else {
// EXT
String mimeType = getMIMEType(str);
System.out.println("Default MIME type for extension '" + str + "' is "
+ (mimeType != null ? ": '" + mimeType + "'" : "unknown") + ".");
System.out.println("All possible: " + getMIMETypes(str));
}
return;
}
Set set = sMIMEToExt.keySet();
String[] mimeTypes = new String[set.size()];
int i = 0;
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
String mime = (String) iterator.next();
mimeTypes[i] = mime;
}
Arrays.sort(mimeTypes);
System.out.println("Known MIME types (" + mimeTypes.length + "):");
for (int j = 0; j < mimeTypes.length; j++) {
String mimeType = mimeTypes[j];
if (j != 0) {
System.out.print(", ");
}
System.out.print(mimeType);
}
System.out.println("\n");
set = sExtToMIME.keySet();
String[] extensions = new String[set.size()];
i = 0;
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
String ext = (String) iterator.next();
extensions[i] = ext;
}
Arrays.sort(extensions);
System.out.println("Known file types (" + extensions.length + "):");
for (int j = 0; j < extensions.length; j++) {
String extension = extensions[j];
if (j != 0) {
System.out.print(", ");
}
System.out.print(extension);
}
System.out.println();
}
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.net;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.lang.SystemUtil;
import java.io.IOException;
import java.util.*;
/**
* Contains mappings from file extension to mime-types and from mime-type to file-types.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/net/MIMEUtil.java#5 $
*
* @see <A href="http://www.iana.org/assignments/media-types/">MIME Media Types</A>
*/
public final class MIMEUtil {
// TODO: Piggy-back on the mappings form the JRE? (1.6 comes with javax.activation)
// TODO: Piggy-back on mappings from javax.activation?
// See: http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/activation/MimetypesFileTypeMap.html
// See: http://java.sun.com/javase/6/docs/api/javax/activation/MimetypesFileTypeMap.html
// TODO: Use the format (and lookup) specified by the above URLs
// TODO: Allow 3rd party to add mappings? Will need application context support to do it safe.. :-P
private static Map<String, List<String>> sExtToMIME = new HashMap<String, List<String>>();
private static Map<String, List<String>> sUnmodifiableExtToMIME = Collections.unmodifiableMap(sExtToMIME);
private static Map<String, List<String>> sMIMEToExt = new HashMap<String, List<String>>();
private static Map<String, List<String>> sUnmodifiableMIMEToExt = Collections.unmodifiableMap(sMIMEToExt);
static {
// Load mapping for MIMEUtil
try {
Properties mappings = SystemUtil.loadProperties(MIMEUtil.class);
for (Map.Entry entry : mappings.entrySet()) {
// Convert and break up extensions and mimeTypes
String extStr = StringUtil.toLowerCase((String) entry.getKey());
List<String> extensions =
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(extStr, ";, ")));
String typeStr = StringUtil.toLowerCase((String) entry.getValue());
List<String> mimeTypes =
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(typeStr, ";, ")));
// TODO: Handle duplicates in MIME to extension mapping, like
// xhtml=application/xhtml+xml;application/xml
// xml=text/xml;application/xml
// Populate normal and reverse MIME-mappings
for (String extension : extensions) {
sExtToMIME.put(extension, mimeTypes);
}
for (String mimeType : mimeTypes) {
sMIMEToExt.put(mimeType, extensions);
}
}
}
catch (IOException e) {
System.err.println("Could not read properties for MIMEUtil: " + e.getMessage());
e.printStackTrace();
}
}
// Disallow construction
private MIMEUtil() {
}
/**
* Returns the default MIME type for the given file extension.
*
* @param pFileExt the file extension
*
* @return a {@code String} containing the MIME type, or {@code null} if
* there are no known MIME types for the given file extension.
*/
public static String getMIMEType(final String pFileExt) {
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
return (types == null || types.isEmpty()) ? null : types.get(0);
}
/**
* Returns all MIME types for the given file extension.
*
* @param pFileExt the file extension
*
* @return a {@link List} of {@code String}s containing the MIME types, or an empty
* list, if there are no known MIME types for the given file extension.
*/
public static List<String> getMIMETypes(final String pFileExt) {
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
return maskNull(types);
}
/**
* Returns an unmodifiabale {@link Map} view of the extension to
* MIME mapping, to use as the default mapping in client applications.
*
* @return an unmodifiabale {@code Map} view of the extension to
* MIME mapping.
*/
public static Map<String, List<String>> getMIMETypeMappings() {
return sUnmodifiableExtToMIME;
}
/**
* Returns the default file extension for the given MIME type.
* Specifying a wildcard type will return {@code null}.
*
* @param pMIME the MIME type
*
* @return a {@code String} containing the file extension, or {@code null}
* if there are no known file extensions for the given MIME type.
*/
public static String getExtension(final String pMIME) {
String mime = bareMIME(StringUtil.toLowerCase(pMIME));
List<String> extensions = sMIMEToExt.get(mime);
return (extensions == null || extensions.isEmpty()) ? null : extensions.get(0);
}
/**
* Returns all file extension for the given MIME type.
* The default extension will be the first in the list.
* Note that no specific order is given for wildcard types (image/*, *&#47;* etc).
*
* @param pMIME the MIME type
*
* @return a {@link List} of {@code String}s containing the MIME types, or an empty
* list, if there are no known file extensions for the given MIME type.
*/
public static List<String> getExtensions(final String pMIME) {
String mime = bareMIME(StringUtil.toLowerCase(pMIME));
if (mime.endsWith("/*")) {
return getExtensionForWildcard(mime);
}
List<String> extensions = sMIMEToExt.get(mime);
return maskNull(extensions);
}
// Gets all extensions for a wildcard MIME type
private static List<String> getExtensionForWildcard(final String pMIME) {
final String family = pMIME.substring(0, pMIME.length() - 1);
Set<String> extensions = new LinkedHashSet<String>();
for (Map.Entry<String, List<String>> mimeToExt : sMIMEToExt.entrySet()) {
if ("*/".equals(family) || mimeToExt.getKey().startsWith(family)) {
extensions.addAll(mimeToExt.getValue());
}
}
return Collections.unmodifiableList(new ArrayList<String>(extensions));
}
/**
* Returns an unmodifiabale {@link Map} view of the MIME to
* extension mapping, to use as the default mapping in client applications.
*
* @return an unmodifiabale {@code Map} view of the MIME to
* extension mapping.
*/
public static Map<String, List<String>> getExtensionMappings() {
return sUnmodifiableMIMEToExt;
}
/**
* Tests wehter the type is a subtype of the type family.
*
* @param pTypeFamily the MIME type family ({@code image/*, *&#47;*}, etc)
* @param pType the MIME type
* @return {@code true} if {@code pType} is a subtype of {@code pTypeFamily}, otherwise {@code false}
*/
// TODO: Rename? isSubtype?
// TODO: Make public
static boolean includes(final String pTypeFamily, final String pType) {
// TODO: Handle null in a well-defined way
// - Is null family same as */*?
// - Is null subtype of any family? Subtype of no family?
String type = bareMIME(pType);
return type.equals(pTypeFamily)
|| "*/*".equals(pTypeFamily)
|| pTypeFamily.endsWith("/*") && pTypeFamily.startsWith(type.substring(0, type.indexOf('/')));
}
/**
* Removes any charset or extra info from the mime-type string (anything after a semicolon, {@code ;}, inclusive).
*
* @param pMIME the mime-type string
* @return the bare mime-type
*/
public static String bareMIME(final String pMIME) {
int idx;
if (pMIME != null && (idx = pMIME.indexOf(';')) >= 0) {
return pMIME.substring(0, idx);
}
return pMIME;
}
// Returns the list or empty list if list is null
private static List<String> maskNull(List<String> pTypes) {
return (pTypes == null) ? Collections.<String>emptyList() : pTypes;
}
/**
* For debugging. Prints all known MIME types and file extensions.
*
* @param pArgs command line arguments
*/
public static void main(String[] pArgs) {
if (pArgs.length > 1) {
String type = pArgs[0];
String family = pArgs[1];
boolean incuded = includes(family, type);
System.out.println(
"Mime type family " + family
+ (incuded ? " includes " : " does not include ")
+ "type " + type
);
}
if (pArgs.length > 0) {
String str = pArgs[0];
if (str.indexOf('/') >= 0) {
// MIME
String extension = getExtension(str);
System.out.println("Default extension for MIME type '" + str + "' is "
+ (extension != null ? ": '" + extension + "'" : "unknown") + ".");
System.out.println("All possible: " + getExtensions(str));
}
else {
// EXT
String mimeType = getMIMEType(str);
System.out.println("Default MIME type for extension '" + str + "' is "
+ (mimeType != null ? ": '" + mimeType + "'" : "unknown") + ".");
System.out.println("All possible: " + getMIMETypes(str));
}
return;
}
Set set = sMIMEToExt.keySet();
String[] mimeTypes = new String[set.size()];
int i = 0;
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
String mime = (String) iterator.next();
mimeTypes[i] = mime;
}
Arrays.sort(mimeTypes);
System.out.println("Known MIME types (" + mimeTypes.length + "):");
for (int j = 0; j < mimeTypes.length; j++) {
String mimeType = mimeTypes[j];
if (j != 0) {
System.out.print(", ");
}
System.out.print(mimeType);
}
System.out.println("\n");
set = sExtToMIME.keySet();
String[] extensions = new String[set.size()];
i = 0;
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
String ext = (String) iterator.next();
extensions[i] = ext;
}
Arrays.sort(extensions);
System.out.println("Known file types (" + extensions.length + "):");
for (int j = 0; j < extensions.length; j++) {
String extension = extensions[j];
if (j != 0) {
System.out.print(", ");
}
System.out.print(extension);
}
System.out.println();
}
}

View File

@@ -102,8 +102,9 @@ public final class DOMSerializer {
/**
* Specifies wether the serializer should use indentation and optimize for
* readability.
* <p/>
* Note: This is a hint, and may be ignored by DOM implemenations.
* <p>
* Note: This is a hint, and may be ignored by DOM implementations.
* </p>
*
* @param pPrettyPrint {@code true} to enable pretty printing
*/