();
+
+ 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;
+ }
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/EmptyReader.java b/common/common-io/src/main/java/com/twelvemonkeys/io/EmptyReader.java
index d83c9053..6ff1a7a4 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/EmptyReader.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/EmptyReader.java
@@ -1,45 +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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @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 Harald Kuhr
+ * @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("");
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/FastByteArrayOutputStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/FastByteArrayOutputStream.java
index bf6c5b5f..87b8fade 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/FastByteArrayOutputStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/FastByteArrayOutputStream.java
@@ -1,134 +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 "TwelveMonkeys" 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 OWNER 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.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.ByteArrayInputStream;
-
-/**
- * An unsynchronized {@code ByteArrayOutputStream} implementation. This version
- * also has a constructor that lets you create a stream with initial content.
- *
- *
- * @author Harald Kuhr
- * @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.
- *
- * 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.
- *
- * 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 Harald Kuhr
+ * @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.
+ *
+ * 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.
+ *
+ * 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);
+ }
}
\ No newline at end of file
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/FileCacheSeekableStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/FileCacheSeekableStream.java
index e514c3c8..d02225d9 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/FileCacheSeekableStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/FileCacheSeekableStream.java
@@ -1,230 +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 "TwelveMonkeys" 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 OWNER 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}.
- *
- * 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 Harald Kuhr
- * @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 {
- 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;
- }
-
- final static class FileCache extends StreamCache {
- private RandomAccessFile mCacheFile;
-
- public FileCache(final File pFile) throws FileNotFoundException {
- Validate.notNull(pFile, "file");
- mCacheFile = new RandomAccessFile(pFile, "rw");
- }
-
- public void write(final int pByte) throws IOException {
- mCacheFile.write(pByte);
- }
-
- @Override
- public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
- mCacheFile.write(pBuffer, pOffset, pLength);
- }
-
- public int read() throws IOException {
- return mCacheFile.read();
- }
-
- @Override
- public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
- return mCacheFile.read(pBuffer, pOffset, pLength);
- }
-
- public void seek(final long pPosition) throws IOException {
- mCacheFile.seek(pPosition);
- }
-
- public long getPosition() throws IOException {
- return mCacheFile.getFilePointer();
- }
- }
-
-}
+/*
+ * 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}.
+ *
+ * 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 Harald Kuhr
+ * @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();
+ }
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/FileSeekableStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/FileSeekableStream.java
index e86ad5f0..c4be9cfd 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/FileSeekableStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/FileSeekableStream.java
@@ -1,130 +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 "TwelveMonkeys" 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 OWNER 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.*;
-
-/**
- * A {@code SeekableInputStream} implementation that uses random access directly to a {@code File}.
- *
- * @see FileCacheSeekableStream
- * @see MemoryCacheSeekableStream
- * @see RandomAccessFile
- *
- * @author Harald Kuhr
- * @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 Harald Kuhr
+ * @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);
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/FileSystem.java b/common/common-io/src/main/java/com/twelvemonkeys/io/FileSystem.java
index 9b18f789..81668a88 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/FileSystem.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/FileSystem.java
@@ -1,101 +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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @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 Harald Kuhr
+ * @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 + ")";
+ }
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/FileUtil.java b/common/common-io/src/main/java/com/twelvemonkeys/io/FileUtil.java
index 8cad4665..5f2953ee 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/FileUtil.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/FileUtil.java
@@ -1,1081 +1,1082 @@
-/*
- * 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 "TwelveMonkeys" 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 OWNER 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.StringUtil;
-import com.twelvemonkeys.lang.Validate;
-import com.twelvemonkeys.util.Visitor;
-
-import java.io.*;
-import java.net.URL;
-import java.text.NumberFormat;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.UndeclaredThrowableException;
-
-/**
- * A utility class with some useful file and i/o related methods.
- *
- * Versions exists take Input and OutputStreams as parameters, to
- * allow for copying streams (URL's etc.).
- *
- * @author Harald Kuhr
- * @author Eirik Torske
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FileUtil.java#3 $
- */
-public final class FileUtil {
- // TODO: Be more cosequent using resolve() all places where File objects are involved
- // TODO: Parameter handling (allow null vs IllegalArgument)
- // TODO: Exception handling
-
- /**
- * The size of the buffer used for copying
- */
- public final static int BUF_SIZE = 1024;
- private static String TEMP_DIR = null;
-
- private final static FileSystem FS = FileSystem.get();
-
- public static void main(String[] pArgs) throws IOException {
- File file;
- if (pArgs[0].startsWith("file:")) {
- file = toFile(new URL(pArgs[0]));
- System.out.println(file);
- }
- else {
- file = new File(pArgs[0]);
- System.out.println(file.toURL());
- }
-
- System.out.println("Free space: " + getFreeSpace(file) + "/" + getTotalSpace(file) + " bytes");
- }
-
- /*
- * Method main for test only.
- *
- public static void main0(String[] pArgs) {
- if (pArgs.length != 2) {
- System.out.println("usage: java Copy in out");
- return;
- }
- try {
- if (!copy(pArgs[0], pArgs[1])) {
- System.out.println("Error copying");
- }
- }
- catch (IOException e) {
- System.out.println(e.getMessage());
- }
- }
- //*/
-
- // Avoid instances/constructor showing up in API doc
- private FileUtil() {}
-
- /**
- * Copies the fromFile to the toFile location. If toFile is a directory, a
- * new file is created in that directory, with the name of the fromFile.
- * If the toFile exists, the file will not be copied, unless owerWrite is
- * true.
- *
- * @param pFromFileName The name of the file to copy from
- * @param pToFileName The name of the file to copy to
- * @return true if the file was copied successfully,
- * false if the output file exists. In all other cases, an
- * IOException is thrown, and the method does not return a value.
- * @throws IOException if an i/o error occurs during copy
- */
- public static boolean copy(String pFromFileName, String pToFileName) throws IOException {
- return copy(new File(pFromFileName), new File(pToFileName), false);
- }
-
- /**
- * Copies the fromFile to the toFile location. If toFile is a directory, a
- * new file is created in that directory, with the name of the fromFile.
- * If the toFile exists, the file will not be copied, unless owerWrite is
- * true.
- *
- * @param pFromFileName The name of the file to copy from
- * @param pToFileName The name of the file to copy to
- * @param pOverWrite Specifies if the toFile should be overwritten, if it
- * exists.
- * @return true if the file was copied successfully,
- * false if the output file exists, and the owerWrite parameter is
- * false. In all other cases, an
- * IOException is thrown, and the method does not return a value.
- * @throws IOException if an i/o error occurs during copy
- */
- public static boolean copy(String pFromFileName, String pToFileName, boolean pOverWrite) throws IOException {
- return copy(new File(pFromFileName), new File(pToFileName), pOverWrite);
- }
-
- /**
- * Copies the fromFile to the toFile location. If toFile is a directory, a
- * new file is created in that directory, with the name of the fromFile.
- * If the toFile exists, the file will not be copied, unless owerWrite is
- * true.
- *
- * @param pFromFile The file to copy from
- * @param pToFile The file to copy to
- * @return true if the file was copied successfully,
- * false if the output file exists. In all other cases, an
- * IOException is thrown, and the method does not return a value.
- * @throws IOException if an i/o error occurs during copy
- */
- public static boolean copy(File pFromFile, File pToFile) throws IOException {
- return copy(pFromFile, pToFile, false);
- }
-
- /**
- * Copies the fromFile to the toFile location. If toFile is a directory, a
- * new file is created in that directory, with the name of the fromFile.
- * If the toFile exists, the file will not be copied, unless owerWrite is
- * true.
- *
- * @param pFromFile The file to copy from
- * @param pToFile The file to copy to
- * @param pOverWrite Specifies if the toFile should be overwritten, if it
- * exists.
- * @return {@code true} if the file was copied successfully,
- * {@code false} if the output file exists, and the
- * {@code pOwerWrite} parameter is
- * {@code false}. In all other cases, an
- * {@code IOExceptio}n is thrown, and the method does not return.
- * @throws IOException if an i/o error occurs during copy
- * @todo Test copyDir functionality!
- */
- public static boolean copy(File pFromFile, File pToFile, boolean pOverWrite) throws IOException {
- // Copy all directory structure
- if (pFromFile.isDirectory()) {
- return copyDir(pFromFile, pToFile, pOverWrite);
- }
-
- // Check if destination is a directory
- if (pToFile.isDirectory()) {
- // Create a new file with same name as from
- pToFile = new File(pToFile, pFromFile.getName());
- }
-
- // Check if file exists, and return false if overWrite is false
- if (!pOverWrite && pToFile.exists()) {
- return false;
- }
-
- InputStream in = null;
- OutputStream out = null;
-
- try {
- // Use buffer size two times byte array, to avoid i/o bottleneck
- in = new FileInputStream(pFromFile);
- out = new FileOutputStream(pToFile);
-
- // Copy from inputStream to outputStream
- copy(in, out);
- }
- //Just pass any IOException on up the stack
- finally {
- close(in);
- close(out);
- }
-
- return true; // If we got here, everything is probably okay.. ;-)
- }
-
- /**
- * Tries to close the given stream.
- * NOTE: If the stream cannot be closed, the IOException thrown is silently
- * ignored.
- *
- * @param pInput the stream to close
- */
- public static void close(InputStream pInput) {
- try {
- if (pInput != null) {
- pInput.close();
- }
- }
- catch (IOException ignore) {
- // Non critical error
- }
- }
-
- /**
- * Tries to close the given stream.
- * NOTE: If the stream cannot be closed, the IOException thrown is silently
- * ignored.
- *
- * @param pOutput the stream to close
- */
- public static void close(OutputStream pOutput) {
- try {
- if (pOutput != null) {
- pOutput.close();
- }
- }
- catch (IOException ignore) {
- // Non critical error
- }
- }
-
- static void close(Reader pReader) {
- try {
- if (pReader != null) {
- pReader.close();
- }
- }
- catch (IOException ignore) {
- // Non critical error
- }
- }
-
- static void close(Writer pWriter) {
- try {
- if (pWriter != null) {
- pWriter.close();
- }
- }
- catch (IOException ignore) {
- // Non critical error
- }
- }
-
- /**
- * Copies a directory recursively. If the destination folder does not exist,
- * it is created
- *
- * @param pFrom the source directory
- * @param pTo the destination directory
- * @param pOverWrite {@code true} if we should allow overwrting existing files
- * @return {@code true} if all files were copied sucessfully
- * @throws IOException if {@code pTo} exists, and it not a directory,
- * or if copying of any of the files in the folder fails
- */
- private static boolean copyDir(File pFrom, File pTo, boolean pOverWrite) throws IOException {
- if (pTo.exists() && !pTo.isDirectory()) {
- throw new IOException("A directory may only be copied to another directory, not to a file");
- }
- pTo.mkdirs(); // mkdir?
- boolean allOkay = true;
- File[] files = pFrom.listFiles();
-
- for (File file : files) {
- if (!copy(file, new File(pTo, file.getName()), pOverWrite)) {
- allOkay = false;
- }
- }
- return allOkay;
- }
-
- /**
- * Copies all data from one stream to another.
- * The data is copied from the fromStream to the toStream using buffered
- * streams for efficiency.
- *
- * @param pFrom The input srteam to copy from
- * @param pTo The output stream to copy to
- * @return true. Otherwise, an
- * IOException is thrown, and the method does not return a value.
- * @throws IOException if an i/o error occurs during copy
- * @throws IllegalArgumentException if either {@code pFrom} or {@code pTo} is
- * {@code null}
- */
- public static boolean copy(InputStream pFrom, OutputStream pTo) throws IOException {
- Validate.notNull(pFrom, "from");
- Validate.notNull(pTo, "to");
-
- // TODO: Consider using file channels for faster copy where possible
-
- // Use buffer size two times byte array, to avoid i/o bottleneck
- // TODO: Consider letting the client decide as this is sometimes not a good thing!
- InputStream in = new BufferedInputStream(pFrom, BUF_SIZE * 2);
- OutputStream out = new BufferedOutputStream(pTo, BUF_SIZE * 2);
-
- byte[] buffer = new byte[BUF_SIZE];
- int count;
-
- while ((count = in.read(buffer)) != -1) {
- out.write(buffer, 0, count);
- }
-
- // Flush out stream, to write any remaining buffered data
- out.flush();
-
- return true; // If we got here, everything is probably okay.. ;-)
- }
-
- /**
- * Gets the file (type) extension of the given file.
- * A file extension is the part of the filename, after the last occurence
- * of a period {@code '.'}.
- * If the filename contains no period, {@code null} is returned.
- *
- * @param pFileName the full filename with extension
- * @return the extension (type) of the file, or {@code null}
- */
- public static String getExtension(final String pFileName) {
- return getExtension0(getFilename(pFileName));
- }
-
- /**
- * Gets the file (type) extension of the given file.
- * A file extension is the part of the filename, after the last occurence
- * of a period {@code '.'}.
- * If the filename contains no period, {@code null} is returned.
- *
- * @param pFile the file
- * @return the extension (type) of the file, or {@code null}
- */
- public static String getExtension(final File pFile) {
- return getExtension0(pFile.getName());
- }
-
- // NOTE: Assumes filename and no path
- private static String getExtension0(final String pFileName) {
- int index = pFileName.lastIndexOf('.');
-
- if (index >= 0) {
- return pFileName.substring(index + 1);
- }
-
- // No period found
- return null;
- }
-
-
- /**
- * Gets the file name of the given file, without the extension (type).
- * A file extension is the part of the filename, after the last occurence
- * of a period {@code '.'}.
- * If the filename contains no period, the complete file name is returned
- * (same as {@code pFileName}, if the string contains no path elements).
- *
- * @param pFileName the full filename with extension
- * @return the base name of the file
- */
- public static String getBasename(final String pFileName) {
- return getBasename0(getFilename(pFileName));
- }
-
- /**
- * Gets the file name of the given file, without the extension (type).
- * A file extension is the part of the filename, after the last occurence
- * of a period {@code '.'}.
- * If the filename contains no period, {@code pFile.getName()} is returned.
- *
- * @param pFile the file
- * @return the base name of the file
- */
- public static String getBasename(final File pFile) {
- return getBasename0(pFile.getName());
- }
-
- // NOTE: Assumes filename and no path
- public static String getBasename0(final String pFileName) {
- int index = pFileName.lastIndexOf('.');
-
- if (index >= 0) {
- return pFileName.substring(0, index);
- }
-
- // No period found
- return pFileName;
- }
-
- /**
- * Extracts the directory path without the filename, from a complete
- * filename path.
- *
- * @param pPath The full filename path.
- * @return the path without the filename.
- * @see File#getParent
- * @see #getFilename
- */
- public static String getDirectoryname(final String pPath) {
- return getDirectoryname(pPath, File.separatorChar);
- }
-
- /**
- * Extracts the directory path without the filename, from a complete
- * filename path.
- *
- * @param pPath The full filename path.
- * @param pSeparator the separator char used in {@code pPath}
- * @return the path without the filename.
- * @see File#getParent
- * @see #getFilename
- */
- public static String getDirectoryname(final String pPath, final char pSeparator) {
- int index = pPath.lastIndexOf(pSeparator);
-
- if (index < 0) {
- return ""; // Assume only filename
- }
- return pPath.substring(0, index);
- }
-
- /**
- * Extracts the filename of a complete filename path.
- *
- * @param pPath The full filename path.
- * @return the extracted filename.
- * @see File#getName
- * @see #getDirectoryname
- */
- public static String getFilename(final String pPath) {
- return getFilename(pPath, File.separatorChar);
- }
-
- /**
- * Extracts the filename of a complete filename path.
- *
- * @param pPath The full filename path.
- * @param pSeparator The file separator.
- * @return the extracted filename.
- * @see File#getName
- * @see #getDirectoryname
- */
- public static String getFilename(final String pPath, final char pSeparator) {
- int index = pPath.lastIndexOf(pSeparator);
-
- if (index < 0) {
- return pPath; // Assume only filename
- }
-
- return pPath.substring(index + 1);
- }
-
-
- /**
- * Tests if a file or directory has no content.
- * A file is empty if it has a length of 0L. A non-existing file is also
- * considered empty.
- * A directory is considered empty if it contains no files.
- *
- * @param pFile The file to test
- * @return {@code true} if the file is empty, otherwise
- * {@code false}.
- */
- public static boolean isEmpty(File pFile) {
- if (pFile.isDirectory()) {
- return (pFile.list().length == 0);
- }
- return (pFile.length() == 0);
- }
-
- /**
- * Gets the default temp directory for the system as a File.
- *
- * @return a {@code File}, representing the default temp directory.
- * @see File#createTempFile
- */
- public static File getTempDirFile() {
- return new File(getTempDir());
- }
-
- /**
- * Gets the default temp directory for the system.
- *
- * @return a {@code String}, representing the path to the default temp
- * directory.
- * @see File#createTempFile
- */
- public static String getTempDir() {
- synchronized (FileUtil.class) {
- if (TEMP_DIR == null) {
- // Get the 'java.io.tmpdir' property
- String tmpDir = System.getProperty("java.io.tmpdir");
-
- if (StringUtil.isEmpty(tmpDir)) {
- // Stupid fallback...
- // TODO: Delegate to FileSystem?
- if (new File("/temp").exists()) {
- tmpDir = "/temp"; // Windows
- }
- else {
- tmpDir = "/tmp"; // Unix
- }
- }
- TEMP_DIR = tmpDir;
- }
- }
- return TEMP_DIR;
- }
-
- /**
- * Gets the contents of the given file, as a byte array.
- *
- * @param pFilename the name of the file to get content from
- * @return the content of the file as a byte array.
- * @throws IOException if the read operation fails
- */
- public static byte[] read(String pFilename) throws IOException {
- return read(new File(pFilename));
- }
-
- /**
- * Gets the contents of the given file, as a byte array.
- *
- * @param pFile the file to get content from
- * @return the content of the file as a byte array.
- * @throws IOException if the read operation fails
- */
- public static byte[] read(File pFile) throws IOException {
- // Custom implementation, as we know the size of a file
- if (!pFile.exists()) {
- throw new FileNotFoundException(pFile.toString());
- }
-
- byte[] bytes = new byte[(int) pFile.length()];
- InputStream in = null;
-
- try {
- // Use buffer size two times byte array, to avoid i/o bottleneck
- in = new BufferedInputStream(new FileInputStream(pFile), BUF_SIZE * 2);
-
- int off = 0;
- int len;
- while ((len = in.read(bytes, off, in.available())) != -1 && (off < bytes.length)) {
- off += len;
- // System.out.println("read:" + len);
- }
- }
- // Just pass any IOException on up the stack
- finally {
- close(in);
- }
-
- return bytes;
- }
-
- /**
- * Reads all data from the input stream to a byte array.
- *
- * @param pInput The input stream to read from
- * @return The content of the stream as a byte array.
- * @throws IOException if an i/o error occurs during read.
- */
- public static byte[] read(InputStream pInput) throws IOException {
- // Create byte array
- ByteArrayOutputStream bytes = new FastByteArrayOutputStream(BUF_SIZE);
-
- // Copy from stream to byte array
- copy(pInput, bytes);
-
- return bytes.toByteArray();
- }
-
- /**
- * Writes the contents from a byte array to an output stream.
- *
- * @param pOutput The output stream to write to
- * @param pData The byte array to write
- * @return {@code true}, otherwise an IOException is thrown.
- * @throws IOException if an i/o error occurs during write.
- */
- public static boolean write(OutputStream pOutput, byte[] pData) throws IOException {
- // Write data
- pOutput.write(pData);
-
- // If we got here, all is okay
- return true;
- }
-
- /**
- * Writes the contents from a byte array to a file.
- *
- * @param pFile The file to write to
- * @param pData The byte array to write
- * @return {@code true}, otherwise an IOException is thrown.
- * @throws IOException if an i/o error occurs during write.
- */
- public static boolean write(File pFile, byte[] pData) throws IOException {
- boolean success = false;
- OutputStream out = null;
-
- try {
- out = new BufferedOutputStream(new FileOutputStream(pFile));
- success = write(out, pData);
- }
- finally {
- close(out);
- }
- return success;
- }
-
- /**
- * Writes the contents from a byte array to a file.
- *
- * @param pFilename The name of the file to write to
- * @param pData The byte array to write
- * @return {@code true}, otherwise an IOException is thrown.
- * @throws IOException if an i/o error occurs during write.
- */
- public static boolean write(String pFilename, byte[] pData) throws IOException {
- return write(new File(pFilename), pData);
- }
-
- /**
- * Deletes the specified file.
- *
- * @param pFile The file to delete
- * @param pForce Forces delete, even if the parameter is a directory, and
- * is not empty. Be careful!
- * @return {@code true}, if the file existed and was deleted.
- * @throws IOException if an i/o error occurs during delete.
- */
- public static boolean delete(final File pFile, final boolean pForce) throws IOException {
- if (pForce && pFile.isDirectory()) {
- return deleteDir(pFile);
- }
- return pFile.exists() && pFile.delete();
- }
-
- /**
- * Deletes a directory recursively.
- *
- * @param pFile the file to delete
- * @return {@code true} if the file was deleted sucessfully
- * @throws IOException if an i/o error occurs during delete.
- */
- private static boolean deleteDir(final File pFile) throws IOException {
- // Recusively delete all files/subfolders
- // Deletes the files using visitor pattern, to avoid allocating
- // a file array, which may throw OutOfMemoryExceptions for
- // large directories/in low memory situations
- class DeleteFilesVisitor implements Visitor {
- private int failedCount = 0;
- private IOException exception = null;
-
- public void visit(final File pFile) {
- try {
- if (!delete(pFile, true)) {
- failedCount++;
- }
- }
- catch (IOException e) {
- failedCount++;
- if (exception == null) {
- exception = e;
- }
- }
- }
-
- boolean succeeded() throws IOException {
- if (exception != null) {
- throw exception;
- }
- return failedCount == 0;
- }
- }
- DeleteFilesVisitor fileDeleter = new DeleteFilesVisitor();
- visitFiles(pFile, null, fileDeleter);
-
- // If any of the deletes above failed, this will fail (or return false)
- return fileDeleter.succeeded() && pFile.delete();
- }
-
- /**
- * Deletes the specified file.
- *
- * @param pFilename The name of file to delete
- * @param pForce Forces delete, even if the parameter is a directory, and
- * is not empty. Careful!
- * @return {@code true}, if the file existed and was deleted.
- * @throws java.io.IOException if deletion fails
- */
- public static boolean delete(String pFilename, boolean pForce) throws IOException {
- return delete(new File(pFilename), pForce);
- }
-
- /**
- * Deletes the specified file.
- *
- * @param pFile The file to delete
- * @return {@code true}, if the file existed and was deleted.
- * @throws java.io.IOException if deletion fails
- */
- public static boolean delete(File pFile) throws IOException {
- return delete(pFile, false);
- }
-
- /**
- * Deletes the specified file.
- *
- * @param pFilename The name of file to delete
- * @return {@code true}, if the file existed and was deleted.
- * @throws java.io.IOException if deletion fails
- */
- public static boolean delete(String pFilename) throws IOException {
- return delete(new File(pFilename), false);
- }
-
- /**
- * Renames the specified file.
- * If the destination is a directory (and the source is not), the source
- * file is simply moved to the destination directory.
- *
- * @param pFrom The file to rename
- * @param pTo The new file
- * @param pOverWrite Specifies if the tofile should be overwritten, if it
- * exists
- * @return {@code true}, if the file was renamed.
- *
- * @throws FileNotFoundException if {@code pFrom} does not exist.
- */
- public static boolean rename(File pFrom, File pTo, boolean pOverWrite) throws IOException {
- if (!pFrom.exists()) {
- throw new FileNotFoundException(pFrom.getAbsolutePath());
- }
-
- if (pFrom.isFile() && pTo.isDirectory()) {
- pTo = new File(pTo, pFrom.getName());
- }
- return (pOverWrite || !pTo.exists()) && pFrom.renameTo(pTo);
-
- }
-
- /**
- * Renames the specified file, if the destination does not exist.
- * If the destination is a directory (and the source is not), the source
- * file is simply moved to the destination directory.
- *
- * @param pFrom The file to rename
- * @param pTo The new file
- * @return {@code true}, if the file was renamed.
- * @throws java.io.IOException if rename fails
- */
- public static boolean rename(File pFrom, File pTo) throws IOException {
- return rename(pFrom, pTo, false);
- }
-
- /**
- * Renames the specified file.
- * If the destination is a directory (and the source is not), the source
- * file is simply moved to the destination directory.
- *
- * @param pFrom The file to rename
- * @param pTo The new name of the file
- * @param pOverWrite Specifies if the tofile should be overwritten, if it
- * exists
- * @return {@code true}, if the file was renamed.
- * @throws java.io.IOException if rename fails
- */
- public static boolean rename(File pFrom, String pTo, boolean pOverWrite) throws IOException {
- return rename(pFrom, new File(pTo), pOverWrite);
- }
-
- /**
- * Renames the specified file, if the destination does not exist.
- * If the destination is a directory (and the source is not), the source
- * file is simply moved to the destination directory.
- *
- * @param pFrom The file to rename
- * @param pTo The new name of the file
- * @return {@code true}, if the file was renamed.
- * @throws java.io.IOException if rename fails
- */
- public static boolean rename(File pFrom, String pTo) throws IOException {
- return rename(pFrom, new File(pTo), false);
- }
-
- /**
- * Renames the specified file.
- * If the destination is a directory (and the source is not), the source
- * file is simply moved to the destination directory.
- *
- * @param pFrom The name of the file to rename
- * @param pTo The new name of the file
- * @param pOverWrite Specifies if the tofile should be overwritten, if it
- * exists
- * @return {@code true}, if the file was renamed.
- * @throws java.io.IOException if rename fails
- */
- public static boolean rename(String pFrom, String pTo, boolean pOverWrite) throws IOException {
- return rename(new File(pFrom), new File(pTo), pOverWrite);
- }
-
- /**
- * Renames the specified file, if the destination does not exist.
- * If the destination is a directory (and the source is not), the source
- * file is simply moved to the destination directory.
- *
- * @param pFrom The name of the file to rename
- * @param pTo The new name of the file
- * @return {@code true}, if the file was renamed.
- * @throws java.io.IOException if rename fails
- */
- public static boolean rename(String pFrom, String pTo) throws IOException {
- return rename(new File(pFrom), new File(pTo), false);
- }
-
- /**
- * Lists all files (and directories) in a specific folder.
- *
- * @param pFolder The folder to list
- * @return a list of {@code java.io.File} objects.
- * @throws FileNotFoundException if {@code pFolder} is not a readable file
- */
- public static File[] list(final String pFolder) throws FileNotFoundException {
- return list(pFolder, null);
- }
-
- /**
- * Lists all files (and directories) in a specific folder which are
- * embraced by the wildcard filename mask provided.
- *
- * @param pFolder The folder to list
- * @param pFilenameMask The wildcard filename mask
- * @return a list of {@code java.io.File} objects.
- * @see File#listFiles(FilenameFilter)
- * @throws FileNotFoundException if {@code pFolder} is not a readable file
- */
- public static File[] list(final String pFolder, final String pFilenameMask) throws FileNotFoundException {
- if (StringUtil.isEmpty(pFolder)) {
- return null;
- }
-
- File folder = resolve(pFolder);
- if (!(/*folder.exists() &&*/folder.isDirectory() && folder.canRead())) {
- // NOTE: exists is implicitly called by isDirectory
- throw new FileNotFoundException("\"" + pFolder + "\" is not a directory or is not readable.");
- }
-
- if (StringUtil.isEmpty(pFilenameMask)) {
- return folder.listFiles();
- }
-
- // TODO: Rewrite to use regexp
-
- FilenameFilter filter = new FilenameMaskFilter(pFilenameMask);
- return folder.listFiles(filter);
- }
-
- /**
- * Creates a {@code File} based on the path part of the URL, for
- * file-protocol ({@code file:}) based URLs.
- *
- * @param pURL the {@code file:} URL
- * @return a new {@code File} object representing the URL
- *
- * @throws NullPointerException if {@code pURL} is {@code null}
- * @throws IllegalArgumentException if {@code pURL} is
- * not a file-protocol URL.
- *
- * @see java.io.File#toURI()
- * @see java.io.File#File(java.net.URI)
- */
- public static File toFile(URL pURL) {
- if (pURL == null) {
- throw new NullPointerException("URL == null");
- }
-
- // NOTE: Precondition tests below is based on the File(URI) constructor,
- // and is most likely overkill...
- // NOTE: A URI is absolute iff it has a scheme component
- // As the scheme has to be "file", this is implicitly tested below
- // NOTE: A URI is opaque iff it is absolute and it's shceme-specific
- // part does not begin with a '/', see below
- if (!"file".equals(pURL.getProtocol())) {
- // URL protocol => URI scheme
- throw new IllegalArgumentException("URL scheme is not \"file\"");
- }
- if (pURL.getAuthority() != null) {
- throw new IllegalArgumentException("URL has an authority component");
- }
- if (pURL.getRef() != null) {
- // URL ref (anchor) => URI fragment
- throw new IllegalArgumentException("URI has a fragment component");
- }
- if (pURL.getQuery() != null) {
- throw new IllegalArgumentException("URL has a query component");
- }
- String path = pURL.getPath();
- if (!path.startsWith("/")) {
- // A URL should never be able to represent an opaque URI, test anyway
- throw new IllegalArgumentException("URI is not hierarchical");
- }
- if (path.equals("")) {
- throw new IllegalArgumentException("URI path component is empty");
- }
-
- // Convert separator, doesn't seem to be neccessary on Windows/Unix,
- // but do it anyway to be compatible...
- if (File.separatorChar != '/') {
- path = path.replace('/', File.separatorChar);
- }
-
- return resolve(path);
- }
-
- public static File resolve(String pPath) {
- return Win32File.wrap(new File(pPath));
- }
-
- public static File resolve(File pPath) {
- return Win32File.wrap(pPath);
- }
-
- public static File resolve(File pParent, String pChild) {
- return Win32File.wrap(new File(pParent, pChild));
- }
-
- public static File[] resolve(File[] pPaths) {
- return Win32File.wrap(pPaths);
- }
-
- // TODO: Handle SecurityManagers in a deterministic way
- // TODO: Exception handling
- // TODO: What happens if the file does not exist?
- public static long getFreeSpace(final File pPath) {
- // NOTE: Allow null, to get space in current/system volume
- File path = pPath != null ? pPath : new File(".");
-
- Long space = getSpace16("getFreeSpace", path);
- if (space != null) {
- return space;
- }
-
- return FS.getFreeSpace(path);
- }
-
- public static long getUsableSpace(final File pPath) {
- // NOTE: Allow null, to get space in current/system volume
- File path = pPath != null ? pPath : new File(".");
-
- Long space = getSpace16("getUsableSpace", path);
- if (space != null) {
- return space;
- }
-
- return getTotalSpace(path);
- }
-
- // TODO: FixMe for Windows, before making it public...
- public static long getTotalSpace(final File pPath) {
- // NOTE: Allow null, to get space in current/system volume
- File path = pPath != null ? pPath : new File(".");
-
- Long space = getSpace16("getTotalSpace", path);
- if (space != null) {
- return space;
- }
-
- return FS.getTotalSpace(path);
- }
-
- private static Long getSpace16(final String pMethodName, final File pPath) {
- try {
- Method freeSpace = File.class.getMethod(pMethodName);
- return (Long) freeSpace.invoke(pPath);
- }
- catch (NoSuchMethodException ignore) {}
- catch (IllegalAccessException ignore) {}
- catch (InvocationTargetException e) {
- Throwable throwable = e.getTargetException();
- if (throwable instanceof SecurityException) {
- throw (SecurityException) throwable;
- }
- throw new UndeclaredThrowableException(throwable);
- }
-
- return null;
- }
-
- /**
- * Formats the given number to a human readable format.
- * Kind of like {@code df -h}.
- *
- * @param pSizeInBytes the size in byte
- * @return a human readable string representation
- */
- public static String toHumanReadableSize(final long pSizeInBytes) {
- // TODO: Rewrite to use String.format?
- if (pSizeInBytes < 1024L) {
- return pSizeInBytes + " Bytes";
- }
- else if (pSizeInBytes < (1024L << 10)) {
- return getSizeFormat().format(pSizeInBytes / (double) (1024L)) + " KB";
- }
- else if (pSizeInBytes < (1024L << 20)) {
- return getSizeFormat().format(pSizeInBytes / (double) (1024L << 10)) + " MB";
- }
- else if (pSizeInBytes < (1024L << 30)) {
- return getSizeFormat().format(pSizeInBytes / (double) (1024L << 20)) + " GB";
- }
- else if (pSizeInBytes < (1024L << 40)) {
- return getSizeFormat().format(pSizeInBytes / (double) (1024L << 30)) + " TB";
- }
- else {
- return getSizeFormat().format(pSizeInBytes / (double) (1024L << 40)) + " PB";
- }
- }
-
- // NumberFormat is not thread-safe, so we stick to thread-confined instances
- private static ThreadLocal sNumberFormat = new ThreadLocal() {
- protected NumberFormat initialValue() {
- NumberFormat format = NumberFormat.getNumberInstance();
- // TODO: Consider making this locale/platform specific, OR a method parameter...
-// format.setMaximumFractionDigits(2);
- format.setMaximumFractionDigits(0);
- return format;
- }
- };
-
- private static NumberFormat getSizeFormat() {
- return sNumberFormat.get();
- }
-
- /**
- * Visits all files in {@code pDirectory}. Optionally filtered through a {@link FileFilter}.
- *
- * @param pDirectory the directory to visit files in
- * @param pFilter the filter, may be {@code null}, meaning all files will be visited
- * @param pVisitor the visitor
- *
- * @throws IllegalArgumentException if either {@code pDirectory} or {@code pVisitor} are {@code null}
- *
- * @see com.twelvemonkeys.util.Visitor
- */
- @SuppressWarnings({"ResultOfMethodCallIgnored"})
- public static void visitFiles(final File pDirectory, final FileFilter pFilter, final Visitor pVisitor) {
- Validate.notNull(pDirectory, "directory");
- Validate.notNull(pVisitor, "visitor");
-
- pDirectory.listFiles(new FileFilter() {
- public boolean accept(final File pFile) {
- if (pFilter == null || pFilter.accept(pFile)) {
- pVisitor.visit(pFile);
- }
-
- return false;
- }
- });
- }
-}
+/*
+ * 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.StringUtil;
+import com.twelvemonkeys.lang.Validate;
+import com.twelvemonkeys.util.Visitor;
+
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.net.URL;
+import java.text.NumberFormat;
+
+/**
+ * A utility class with some useful file and i/o related methods.
+ *
+ * Versions exists take Input and OutputStreams as parameters, to
+ * allow for copying streams (URL's etc.).
+ *
+ * @author Harald Kuhr
+ * @author Eirik Torske
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FileUtil.java#3 $
+ */
+public final class FileUtil {
+ // TODO: Be more cosequent using resolve() all places where File objects are involved
+ // TODO: Parameter handling (allow null vs IllegalArgument)
+ // TODO: Exception handling
+
+ /**
+ * The size of the buffer used for copying
+ */
+ public final static int BUF_SIZE = 1024;
+ private static String TEMP_DIR = null;
+
+ private final static FileSystem FS = FileSystem.get();
+
+ public static void main(String[] pArgs) throws IOException {
+ File file;
+ if (pArgs[0].startsWith("file:")) {
+ file = toFile(new URL(pArgs[0]));
+ System.out.println(file);
+ }
+ else {
+ file = new File(pArgs[0]);
+ System.out.println(file.toURL());
+ }
+
+ System.out.println("Free space: " + getFreeSpace(file) + "/" + getTotalSpace(file) + " bytes");
+ }
+
+ /*
+ * Method main for test only.
+ *
+ public static void main0(String[] pArgs) {
+ if (pArgs.length != 2) {
+ System.out.println("usage: java Copy in out");
+ return;
+ }
+ try {
+ if (!copy(pArgs[0], pArgs[1])) {
+ System.out.println("Error copying");
+ }
+ }
+ catch (IOException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+ //*/
+
+ // Avoid instances/constructor showing up in API doc
+ private FileUtil() {}
+
+ /**
+ * Copies the fromFile to the toFile location. If toFile is a directory, a
+ * new file is created in that directory, with the name of the fromFile.
+ * If the toFile exists, the file will not be copied, unless owerWrite is
+ * true.
+ *
+ * @param pFromFileName The name of the file to copy from
+ * @param pToFileName The name of the file to copy to
+ * @return true if the file was copied successfully,
+ * false if the output file exists. In all other cases, an
+ * IOException is thrown, and the method does not return a value.
+ * @throws IOException if an i/o error occurs during copy
+ */
+ public static boolean copy(String pFromFileName, String pToFileName) throws IOException {
+ return copy(new File(pFromFileName), new File(pToFileName), false);
+ }
+
+ /**
+ * Copies the fromFile to the toFile location. If toFile is a directory, a
+ * new file is created in that directory, with the name of the fromFile.
+ * If the toFile exists, the file will not be copied, unless owerWrite is
+ * true.
+ *
+ * @param pFromFileName The name of the file to copy from
+ * @param pToFileName The name of the file to copy to
+ * @param pOverWrite Specifies if the toFile should be overwritten, if it
+ * exists.
+ * @return true if the file was copied successfully,
+ * false if the output file exists, and the owerWrite parameter is
+ * false. In all other cases, an
+ * IOException is thrown, and the method does not return a value.
+ * @throws IOException if an i/o error occurs during copy
+ */
+ public static boolean copy(String pFromFileName, String pToFileName, boolean pOverWrite) throws IOException {
+ return copy(new File(pFromFileName), new File(pToFileName), pOverWrite);
+ }
+
+ /**
+ * Copies the fromFile to the toFile location. If toFile is a directory, a
+ * new file is created in that directory, with the name of the fromFile.
+ * If the toFile exists, the file will not be copied, unless owerWrite is
+ * true.
+ *
+ * @param pFromFile The file to copy from
+ * @param pToFile The file to copy to
+ * @return true if the file was copied successfully,
+ * false if the output file exists. In all other cases, an
+ * IOException is thrown, and the method does not return a value.
+ * @throws IOException if an i/o error occurs during copy
+ */
+ public static boolean copy(File pFromFile, File pToFile) throws IOException {
+ return copy(pFromFile, pToFile, false);
+ }
+
+ /**
+ * Copies the fromFile to the toFile location. If toFile is a directory, a
+ * new file is created in that directory, with the name of the fromFile.
+ * If the toFile exists, the file will not be copied, unless owerWrite is
+ * true.
+ *
+ * @param pFromFile The file to copy from
+ * @param pToFile The file to copy to
+ * @param pOverWrite Specifies if the toFile should be overwritten, if it
+ * exists.
+ * @return {@code true} if the file was copied successfully,
+ * {@code false} if the output file exists, and the
+ * {@code pOwerWrite} parameter is
+ * {@code false}. In all other cases, an
+ * {@code IOExceptio}n is thrown, and the method does not return.
+ * @throws IOException if an i/o error occurs during copy
+ */
+ public static boolean copy(File pFromFile, File pToFile, boolean pOverWrite) throws IOException {
+ // Copy all directory structure
+ if (pFromFile.isDirectory()) {
+ return copyDir(pFromFile, pToFile, pOverWrite);
+ }
+
+ // Check if destination is a directory
+ if (pToFile.isDirectory()) {
+ // Create a new file with same name as from
+ pToFile = new File(pToFile, pFromFile.getName());
+ }
+
+ // Check if file exists, and return false if overWrite is false
+ if (!pOverWrite && pToFile.exists()) {
+ return false;
+ }
+
+ InputStream in = null;
+ OutputStream out = null;
+
+ try {
+ // Use buffer size two times byte array, to avoid i/o bottleneck
+ in = new FileInputStream(pFromFile);
+ out = new FileOutputStream(pToFile);
+
+ // Copy from inputStream to outputStream
+ copy(in, out);
+ }
+ //Just pass any IOException on up the stack
+ finally {
+ close(in);
+ close(out);
+ }
+
+ return true; // If we got here, everything is probably okay.. ;-)
+ }
+
+ /**
+ * Tries to close the given stream.
+ * NOTE: If the stream cannot be closed, the IOException thrown is silently
+ * ignored.
+ *
+ * @param pInput the stream to close
+ */
+ public static void close(InputStream pInput) {
+ try {
+ if (pInput != null) {
+ pInput.close();
+ }
+ }
+ catch (IOException ignore) {
+ // Non critical error
+ }
+ }
+
+ /**
+ * Tries to close the given stream.
+ * NOTE: If the stream cannot be closed, the IOException thrown is silently
+ * ignored.
+ *
+ * @param pOutput the stream to close
+ */
+ public static void close(OutputStream pOutput) {
+ try {
+ if (pOutput != null) {
+ pOutput.close();
+ }
+ }
+ catch (IOException ignore) {
+ // Non critical error
+ }
+ }
+
+ static void close(Reader pReader) {
+ try {
+ if (pReader != null) {
+ pReader.close();
+ }
+ }
+ catch (IOException ignore) {
+ // Non critical error
+ }
+ }
+
+ static void close(Writer pWriter) {
+ try {
+ if (pWriter != null) {
+ pWriter.close();
+ }
+ }
+ catch (IOException ignore) {
+ // Non critical error
+ }
+ }
+
+ /**
+ * Copies a directory recursively. If the destination folder does not exist,
+ * it is created
+ *
+ * @param pFrom the source directory
+ * @param pTo the destination directory
+ * @param pOverWrite {@code true} if we should allow overwrting existing files
+ * @return {@code true} if all files were copied sucessfully
+ * @throws IOException if {@code pTo} exists, and it not a directory,
+ * or if copying of any of the files in the folder fails
+ */
+ private static boolean copyDir(File pFrom, File pTo, boolean pOverWrite) throws IOException {
+ if (pTo.exists() && !pTo.isDirectory()) {
+ throw new IOException("A directory may only be copied to another directory, not to a file");
+ }
+ pTo.mkdirs(); // mkdir?
+ boolean allOkay = true;
+ File[] files = pFrom.listFiles();
+
+ for (File file : files) {
+ if (!copy(file, new File(pTo, file.getName()), pOverWrite)) {
+ allOkay = false;
+ }
+ }
+ return allOkay;
+ }
+
+ /**
+ * Copies all data from one stream to another.
+ * The data is copied from the fromStream to the toStream using buffered
+ * streams for efficiency.
+ *
+ * @param pFrom The input srteam to copy from
+ * @param pTo The output stream to copy to
+ * @return true. Otherwise, an
+ * IOException is thrown, and the method does not return a value.
+ * @throws IOException if an i/o error occurs during copy
+ * @throws IllegalArgumentException if either {@code pFrom} or {@code pTo} is
+ * {@code null}
+ */
+ public static boolean copy(InputStream pFrom, OutputStream pTo) throws IOException {
+ Validate.notNull(pFrom, "from");
+ Validate.notNull(pTo, "to");
+
+ // TODO: Consider using file channels for faster copy where possible
+
+ // Use buffer size two times byte array, to avoid i/o bottleneck
+ // TODO: Consider letting the client decide as this is sometimes not a good thing!
+ InputStream in = new BufferedInputStream(pFrom, BUF_SIZE * 2);
+ OutputStream out = new BufferedOutputStream(pTo, BUF_SIZE * 2);
+
+ byte[] buffer = new byte[BUF_SIZE];
+ int count;
+
+ while ((count = in.read(buffer)) != -1) {
+ out.write(buffer, 0, count);
+ }
+
+ // Flush out stream, to write any remaining buffered data
+ out.flush();
+
+ return true; // If we got here, everything is probably okay.. ;-)
+ }
+
+ /**
+ * Gets the file (type) extension of the given file.
+ * A file extension is the part of the filename, after the last occurence
+ * of a period {@code '.'}.
+ * If the filename contains no period, {@code null} is returned.
+ *
+ * @param pFileName the full filename with extension
+ * @return the extension (type) of the file, or {@code null}
+ */
+ public static String getExtension(final String pFileName) {
+ return getExtension0(getFilename(pFileName));
+ }
+
+ /**
+ * Gets the file (type) extension of the given file.
+ * A file extension is the part of the filename, after the last occurrence
+ * of a period {@code '.'}.
+ * If the filename contains no period, {@code null} is returned.
+ *
+ * @param pFile the file
+ * @return the extension (type) of the file, or {@code null}
+ */
+ public static String getExtension(final File pFile) {
+ return getExtension0(pFile.getName());
+ }
+
+ // NOTE: Assumes filename and no path
+ private static String getExtension0(final String pFileName) {
+ int index = pFileName.lastIndexOf('.');
+
+ if (index >= 0) {
+ return pFileName.substring(index + 1);
+ }
+
+ // No period found
+ return null;
+ }
+
+
+ /**
+ * Gets the file name of the given file, without the extension (type).
+ * A file extension is the part of the filename, after the last occurence
+ * of a period {@code '.'}.
+ * If the filename contains no period, the complete file name is returned
+ * (same as {@code pFileName}, if the string contains no path elements).
+ *
+ * @param pFileName the full filename with extension
+ * @return the base name of the file
+ */
+ public static String getBasename(final String pFileName) {
+ return getBasename0(getFilename(pFileName));
+ }
+
+ /**
+ * Gets the file name of the given file, without the extension (type).
+ * A file extension is the part of the filename, after the last occurence
+ * of a period {@code '.'}.
+ * If the filename contains no period, {@code pFile.getName()} is returned.
+ *
+ * @param pFile the file
+ * @return the base name of the file
+ */
+ public static String getBasename(final File pFile) {
+ return getBasename0(pFile.getName());
+ }
+
+ // NOTE: Assumes filename and no path
+ public static String getBasename0(final String pFileName) {
+ int index = pFileName.lastIndexOf('.');
+
+ if (index >= 0) {
+ return pFileName.substring(0, index);
+ }
+
+ // No period found
+ return pFileName;
+ }
+
+ /**
+ * Extracts the directory path without the filename, from a complete
+ * filename path.
+ *
+ * @param pPath The full filename path.
+ * @return the path without the filename.
+ * @see File#getParent
+ * @see #getFilename
+ */
+ public static String getDirectoryname(final String pPath) {
+ return getDirectoryname(pPath, File.separatorChar);
+ }
+
+ /**
+ * Extracts the directory path without the filename, from a complete
+ * filename path.
+ *
+ * @param pPath The full filename path.
+ * @param pSeparator the separator char used in {@code pPath}
+ * @return the path without the filename.
+ * @see File#getParent
+ * @see #getFilename
+ */
+ public static String getDirectoryname(final String pPath, final char pSeparator) {
+ int index = pPath.lastIndexOf(pSeparator);
+
+ if (index < 0) {
+ return ""; // Assume only filename
+ }
+ return pPath.substring(0, index);
+ }
+
+ /**
+ * Extracts the filename of a complete filename path.
+ *
+ * @param pPath The full filename path.
+ * @return the extracted filename.
+ * @see File#getName
+ * @see #getDirectoryname
+ */
+ public static String getFilename(final String pPath) {
+ return getFilename(pPath, File.separatorChar);
+ }
+
+ /**
+ * Extracts the filename of a complete filename path.
+ *
+ * @param pPath The full filename path.
+ * @param pSeparator The file separator.
+ * @return the extracted filename.
+ * @see File#getName
+ * @see #getDirectoryname
+ */
+ public static String getFilename(final String pPath, final char pSeparator) {
+ int index = pPath.lastIndexOf(pSeparator);
+
+ if (index < 0) {
+ return pPath; // Assume only filename
+ }
+
+ return pPath.substring(index + 1);
+ }
+
+
+ /**
+ * Tests if a file or directory has no content.
+ * A file is empty if it has a length of 0L. A non-existing file is also
+ * considered empty.
+ * A directory is considered empty if it contains no files.
+ *
+ * @param pFile The file to test
+ * @return {@code true} if the file is empty, otherwise
+ * {@code false}.
+ */
+ public static boolean isEmpty(File pFile) {
+ if (pFile.isDirectory()) {
+ return (pFile.list().length == 0);
+ }
+ return (pFile.length() == 0);
+ }
+
+ /**
+ * Gets the default temp directory for the system as a File.
+ *
+ * @return a {@code File}, representing the default temp directory.
+ * @see File#createTempFile
+ */
+ public static File getTempDirFile() {
+ return new File(getTempDir());
+ }
+
+ /**
+ * Gets the default temp directory for the system.
+ *
+ * @return a {@code String}, representing the path to the default temp
+ * directory.
+ * @see File#createTempFile
+ */
+ public static String getTempDir() {
+ synchronized (FileUtil.class) {
+ if (TEMP_DIR == null) {
+ // Get the 'java.io.tmpdir' property
+ String tmpDir = System.getProperty("java.io.tmpdir");
+
+ if (StringUtil.isEmpty(tmpDir)) {
+ // Stupid fallback...
+ // TODO: Delegate to FileSystem?
+ if (new File("/temp").exists()) {
+ tmpDir = "/temp"; // Windows
+ }
+ else {
+ tmpDir = "/tmp"; // Unix
+ }
+ }
+ TEMP_DIR = tmpDir;
+ }
+ }
+ return TEMP_DIR;
+ }
+
+ /**
+ * Gets the contents of the given file, as a byte array.
+ *
+ * @param pFilename the name of the file to get content from
+ * @return the content of the file as a byte array.
+ * @throws IOException if the read operation fails
+ */
+ public static byte[] read(String pFilename) throws IOException {
+ return read(new File(pFilename));
+ }
+
+ /**
+ * Gets the contents of the given file, as a byte array.
+ *
+ * @param pFile the file to get content from
+ * @return the content of the file as a byte array.
+ * @throws IOException if the read operation fails
+ */
+ public static byte[] read(File pFile) throws IOException {
+ // Custom implementation, as we know the size of a file
+ if (!pFile.exists()) {
+ throw new FileNotFoundException(pFile.toString());
+ }
+
+ byte[] bytes = new byte[(int) pFile.length()];
+ InputStream in = null;
+
+ try {
+ // Use buffer size two times byte array, to avoid i/o bottleneck
+ in = new BufferedInputStream(new FileInputStream(pFile), BUF_SIZE * 2);
+
+ int off = 0;
+ int len;
+ while ((len = in.read(bytes, off, in.available())) != -1 && (off < bytes.length)) {
+ off += len;
+ // System.out.println("read:" + len);
+ }
+ }
+ // Just pass any IOException on up the stack
+ finally {
+ close(in);
+ }
+
+ return bytes;
+ }
+
+ /**
+ * Reads all data from the input stream to a byte array.
+ *
+ * @param pInput The input stream to read from
+ * @return The content of the stream as a byte array.
+ * @throws IOException if an i/o error occurs during read.
+ */
+ public static byte[] read(InputStream pInput) throws IOException {
+ // Create byte array
+ ByteArrayOutputStream bytes = new FastByteArrayOutputStream(BUF_SIZE);
+
+ // Copy from stream to byte array
+ copy(pInput, bytes);
+
+ return bytes.toByteArray();
+ }
+
+ /**
+ * Writes the contents from a byte array to an output stream.
+ *
+ * @param pOutput The output stream to write to
+ * @param pData The byte array to write
+ * @return {@code true}, otherwise an IOException is thrown.
+ * @throws IOException if an i/o error occurs during write.
+ */
+ public static boolean write(OutputStream pOutput, byte[] pData) throws IOException {
+ // Write data
+ pOutput.write(pData);
+
+ // If we got here, all is okay
+ return true;
+ }
+
+ /**
+ * Writes the contents from a byte array to a file.
+ *
+ * @param pFile The file to write to
+ * @param pData The byte array to write
+ * @return {@code true}, otherwise an IOException is thrown.
+ * @throws IOException if an i/o error occurs during write.
+ */
+ public static boolean write(File pFile, byte[] pData) throws IOException {
+ boolean success = false;
+ OutputStream out = null;
+
+ try {
+ out = new BufferedOutputStream(new FileOutputStream(pFile));
+ success = write(out, pData);
+ }
+ finally {
+ close(out);
+ }
+ return success;
+ }
+
+ /**
+ * Writes the contents from a byte array to a file.
+ *
+ * @param pFilename The name of the file to write to
+ * @param pData The byte array to write
+ * @return {@code true}, otherwise an IOException is thrown.
+ * @throws IOException if an i/o error occurs during write.
+ */
+ public static boolean write(String pFilename, byte[] pData) throws IOException {
+ return write(new File(pFilename), pData);
+ }
+
+ /**
+ * Deletes the specified file.
+ *
+ * @param pFile The file to delete
+ * @param pForce Forces delete, even if the parameter is a directory, and
+ * is not empty. Be careful!
+ * @return {@code true}, if the file existed and was deleted.
+ * @throws IOException if an i/o error occurs during delete.
+ */
+ public static boolean delete(final File pFile, final boolean pForce) throws IOException {
+ if (pForce && pFile.isDirectory()) {
+ return deleteDir(pFile);
+ }
+ return pFile.exists() && pFile.delete();
+ }
+
+ /**
+ * Deletes a directory recursively.
+ *
+ * @param pFile the file to delete
+ * @return {@code true} if the file was deleted sucessfully
+ * @throws IOException if an i/o error occurs during delete.
+ */
+ private static boolean deleteDir(final File pFile) throws IOException {
+ // Recusively delete all files/subfolders
+ // Deletes the files using visitor pattern, to avoid allocating
+ // a file array, which may throw OutOfMemoryExceptions for
+ // large directories/in low memory situations
+ class DeleteFilesVisitor implements Visitor {
+ private int failedCount = 0;
+ private IOException exception = null;
+
+ public void visit(final File pFile) {
+ try {
+ if (!delete(pFile, true)) {
+ failedCount++;
+ }
+ }
+ catch (IOException e) {
+ failedCount++;
+ if (exception == null) {
+ exception = e;
+ }
+ }
+ }
+
+ boolean succeeded() throws IOException {
+ if (exception != null) {
+ throw exception;
+ }
+ return failedCount == 0;
+ }
+ }
+ DeleteFilesVisitor fileDeleter = new DeleteFilesVisitor();
+ visitFiles(pFile, null, fileDeleter);
+
+ // If any of the deletes above failed, this will fail (or return false)
+ return fileDeleter.succeeded() && pFile.delete();
+ }
+
+ /**
+ * Deletes the specified file.
+ *
+ * @param pFilename The name of file to delete
+ * @param pForce Forces delete, even if the parameter is a directory, and
+ * is not empty. Careful!
+ * @return {@code true}, if the file existed and was deleted.
+ * @throws java.io.IOException if deletion fails
+ */
+ public static boolean delete(String pFilename, boolean pForce) throws IOException {
+ return delete(new File(pFilename), pForce);
+ }
+
+ /**
+ * Deletes the specified file.
+ *
+ * @param pFile The file to delete
+ * @return {@code true}, if the file existed and was deleted.
+ * @throws java.io.IOException if deletion fails
+ */
+ public static boolean delete(File pFile) throws IOException {
+ return delete(pFile, false);
+ }
+
+ /**
+ * Deletes the specified file.
+ *
+ * @param pFilename The name of file to delete
+ * @return {@code true}, if the file existed and was deleted.
+ * @throws java.io.IOException if deletion fails
+ */
+ public static boolean delete(String pFilename) throws IOException {
+ return delete(new File(pFilename), false);
+ }
+
+ /**
+ * Renames the specified file.
+ * If the destination is a directory (and the source is not), the source
+ * file is simply moved to the destination directory.
+ *
+ * @param pFrom The file to rename
+ * @param pTo The new file
+ * @param pOverWrite Specifies if the tofile should be overwritten, if it
+ * exists
+ * @return {@code true}, if the file was renamed.
+ *
+ * @throws FileNotFoundException if {@code pFrom} does not exist.
+ */
+ public static boolean rename(File pFrom, File pTo, boolean pOverWrite) throws IOException {
+ if (!pFrom.exists()) {
+ throw new FileNotFoundException(pFrom.getAbsolutePath());
+ }
+
+ if (pFrom.isFile() && pTo.isDirectory()) {
+ pTo = new File(pTo, pFrom.getName());
+ }
+ return (pOverWrite || !pTo.exists()) && pFrom.renameTo(pTo);
+
+ }
+
+ /**
+ * Renames the specified file, if the destination does not exist.
+ * If the destination is a directory (and the source is not), the source
+ * file is simply moved to the destination directory.
+ *
+ * @param pFrom The file to rename
+ * @param pTo The new file
+ * @return {@code true}, if the file was renamed.
+ * @throws java.io.IOException if rename fails
+ */
+ public static boolean rename(File pFrom, File pTo) throws IOException {
+ return rename(pFrom, pTo, false);
+ }
+
+ /**
+ * Renames the specified file.
+ * If the destination is a directory (and the source is not), the source
+ * file is simply moved to the destination directory.
+ *
+ * @param pFrom The file to rename
+ * @param pTo The new name of the file
+ * @param pOverWrite Specifies if the tofile should be overwritten, if it
+ * exists
+ * @return {@code true}, if the file was renamed.
+ * @throws java.io.IOException if rename fails
+ */
+ public static boolean rename(File pFrom, String pTo, boolean pOverWrite) throws IOException {
+ return rename(pFrom, new File(pTo), pOverWrite);
+ }
+
+ /**
+ * Renames the specified file, if the destination does not exist.
+ * If the destination is a directory (and the source is not), the source
+ * file is simply moved to the destination directory.
+ *
+ * @param pFrom The file to rename
+ * @param pTo The new name of the file
+ * @return {@code true}, if the file was renamed.
+ * @throws java.io.IOException if rename fails
+ */
+ public static boolean rename(File pFrom, String pTo) throws IOException {
+ return rename(pFrom, new File(pTo), false);
+ }
+
+ /**
+ * Renames the specified file.
+ * If the destination is a directory (and the source is not), the source
+ * file is simply moved to the destination directory.
+ *
+ * @param pFrom The name of the file to rename
+ * @param pTo The new name of the file
+ * @param pOverWrite Specifies if the tofile should be overwritten, if it
+ * exists
+ * @return {@code true}, if the file was renamed.
+ * @throws java.io.IOException if rename fails
+ */
+ public static boolean rename(String pFrom, String pTo, boolean pOverWrite) throws IOException {
+ return rename(new File(pFrom), new File(pTo), pOverWrite);
+ }
+
+ /**
+ * Renames the specified file, if the destination does not exist.
+ * If the destination is a directory (and the source is not), the source
+ * file is simply moved to the destination directory.
+ *
+ * @param pFrom The name of the file to rename
+ * @param pTo The new name of the file
+ * @return {@code true}, if the file was renamed.
+ * @throws java.io.IOException if rename fails
+ */
+ public static boolean rename(String pFrom, String pTo) throws IOException {
+ return rename(new File(pFrom), new File(pTo), false);
+ }
+
+ /**
+ * Lists all files (and directories) in a specific folder.
+ *
+ * @param pFolder The folder to list
+ * @return a list of {@code java.io.File} objects.
+ * @throws FileNotFoundException if {@code pFolder} is not a readable file
+ */
+ public static File[] list(final String pFolder) throws FileNotFoundException {
+ return list(pFolder, null);
+ }
+
+ /**
+ * Lists all files (and directories) in a specific folder which are
+ * embraced by the wildcard filename mask provided.
+ *
+ * @param pFolder The folder to list
+ * @param pFilenameMask The wildcard filename mask
+ * @return a list of {@code java.io.File} objects.
+ * @see File#listFiles(FilenameFilter)
+ * @throws FileNotFoundException if {@code pFolder} is not a readable file
+ */
+ public static File[] list(final String pFolder, final String pFilenameMask) throws FileNotFoundException {
+ if (StringUtil.isEmpty(pFolder)) {
+ return null;
+ }
+
+ File folder = resolve(pFolder);
+ if (!(/*folder.exists() &&*/folder.isDirectory() && folder.canRead())) {
+ // NOTE: exists is implicitly called by isDirectory
+ throw new FileNotFoundException("\"" + pFolder + "\" is not a directory or is not readable.");
+ }
+
+ if (StringUtil.isEmpty(pFilenameMask)) {
+ return folder.listFiles();
+ }
+
+ // TODO: Rewrite to use regexp
+
+ FilenameFilter filter = new FilenameMaskFilter(pFilenameMask);
+ return folder.listFiles(filter);
+ }
+
+ /**
+ * Creates a {@code File} based on the path part of the URL, for
+ * file-protocol ({@code file:}) based URLs.
+ *
+ * @param pURL the {@code file:} URL
+ * @return a new {@code File} object representing the URL
+ *
+ * @throws NullPointerException if {@code pURL} is {@code null}
+ * @throws IllegalArgumentException if {@code pURL} is
+ * not a file-protocol URL.
+ *
+ * @see java.io.File#toURI()
+ * @see java.io.File#File(java.net.URI)
+ */
+ public static File toFile(URL pURL) {
+ if (pURL == null) {
+ throw new NullPointerException("URL == null");
+ }
+
+ // NOTE: Precondition tests below is based on the File(URI) constructor,
+ // and is most likely overkill...
+ // NOTE: A URI is absolute iff it has a scheme component
+ // As the scheme has to be "file", this is implicitly tested below
+ // NOTE: A URI is opaque iff it is absolute and it's shceme-specific
+ // part does not begin with a '/', see below
+ if (!"file".equals(pURL.getProtocol())) {
+ // URL protocol => URI scheme
+ throw new IllegalArgumentException("URL scheme is not \"file\"");
+ }
+ if (pURL.getAuthority() != null) {
+ throw new IllegalArgumentException("URL has an authority component");
+ }
+ if (pURL.getRef() != null) {
+ // URL ref (anchor) => URI fragment
+ throw new IllegalArgumentException("URI has a fragment component");
+ }
+ if (pURL.getQuery() != null) {
+ throw new IllegalArgumentException("URL has a query component");
+ }
+ String path = pURL.getPath();
+ if (!path.startsWith("/")) {
+ // A URL should never be able to represent an opaque URI, test anyway
+ throw new IllegalArgumentException("URI is not hierarchical");
+ }
+ if (path.isEmpty()) {
+ throw new IllegalArgumentException("URI path component is empty");
+ }
+
+ // Convert separator, doesn't seem to be neccessary on Windows/Unix,
+ // but do it anyway to be compatible...
+ if (File.separatorChar != '/') {
+ path = path.replace('/', File.separatorChar);
+ }
+
+ return resolve(path);
+ }
+
+ public static File resolve(String pPath) {
+ return Win32File.wrap(new File(pPath));
+ }
+
+ public static File resolve(File pPath) {
+ return Win32File.wrap(pPath);
+ }
+
+ public static File resolve(File pParent, String pChild) {
+ return Win32File.wrap(new File(pParent, pChild));
+ }
+
+ public static File[] resolve(File[] pPaths) {
+ return Win32File.wrap(pPaths);
+ }
+
+ // TODO: Handle SecurityManagers in a deterministic way
+ // TODO: Exception handling
+ // TODO: What happens if the file does not exist?
+ public static long getFreeSpace(final File pPath) {
+ // NOTE: Allow null, to get space in current/system volume
+ File path = pPath != null ? pPath : new File(".");
+
+ Long space = getSpace16("getFreeSpace", path);
+ if (space != null) {
+ return space;
+ }
+
+ return FS.getFreeSpace(path);
+ }
+
+ public static long getUsableSpace(final File pPath) {
+ // NOTE: Allow null, to get space in current/system volume
+ File path = pPath != null ? pPath : new File(".");
+
+ Long space = getSpace16("getUsableSpace", path);
+ if (space != null) {
+ return space;
+ }
+
+ return getTotalSpace(path);
+ }
+
+ // TODO: FixMe for Windows, before making it public...
+ public static long getTotalSpace(final File pPath) {
+ // NOTE: Allow null, to get space in current/system volume
+ File path = pPath != null ? pPath : new File(".");
+
+ Long space = getSpace16("getTotalSpace", path);
+ if (space != null) {
+ return space;
+ }
+
+ return FS.getTotalSpace(path);
+ }
+
+ private static Long getSpace16(final String pMethodName, final File pPath) {
+ try {
+ Method freeSpace = File.class.getMethod(pMethodName);
+ return (Long) freeSpace.invoke(pPath);
+ }
+ catch (NoSuchMethodException ignore) {}
+ catch (IllegalAccessException ignore) {}
+ catch (InvocationTargetException e) {
+ Throwable throwable = e.getTargetException();
+ if (throwable instanceof SecurityException) {
+ throw (SecurityException) throwable;
+ }
+ throw new UndeclaredThrowableException(throwable);
+ }
+
+ return null;
+ }
+
+ /**
+ * Formats the given number to a human readable format.
+ * Kind of like {@code df -h}.
+ *
+ * @param pSizeInBytes the size in byte
+ * @return a human readable string representation
+ */
+ public static String toHumanReadableSize(final long pSizeInBytes) {
+ // TODO: Rewrite to use String.format?
+ if (pSizeInBytes < 1024L) {
+ return pSizeInBytes + " Bytes";
+ }
+ else if (pSizeInBytes < (1024L << 10)) {
+ return getSizeFormat().format(pSizeInBytes / (double) (1024L)) + " KB";
+ }
+ else if (pSizeInBytes < (1024L << 20)) {
+ return getSizeFormat().format(pSizeInBytes / (double) (1024L << 10)) + " MB";
+ }
+ else if (pSizeInBytes < (1024L << 30)) {
+ return getSizeFormat().format(pSizeInBytes / (double) (1024L << 20)) + " GB";
+ }
+ else if (pSizeInBytes < (1024L << 40)) {
+ return getSizeFormat().format(pSizeInBytes / (double) (1024L << 30)) + " TB";
+ }
+ else {
+ return getSizeFormat().format(pSizeInBytes / (double) (1024L << 40)) + " PB";
+ }
+ }
+
+ // NumberFormat is not thread-safe, so we stick to thread-confined instances
+ private static ThreadLocal sNumberFormat = new ThreadLocal() {
+ protected NumberFormat initialValue() {
+ NumberFormat format = NumberFormat.getNumberInstance();
+ // TODO: Consider making this locale/platform specific, OR a method parameter...
+// format.setMaximumFractionDigits(2);
+ format.setMaximumFractionDigits(0);
+ return format;
+ }
+ };
+
+ private static NumberFormat getSizeFormat() {
+ return sNumberFormat.get();
+ }
+
+ /**
+ * Visits all files in {@code pDirectory}. Optionally filtered through a {@link FileFilter}.
+ *
+ * @param pDirectory the directory to visit files in
+ * @param pFilter the filter, may be {@code null}, meaning all files will be visited
+ * @param pVisitor the visitor
+ *
+ * @throws IllegalArgumentException if either {@code pDirectory} or {@code pVisitor} are {@code null}
+ *
+ * @see com.twelvemonkeys.util.Visitor
+ */
+ @SuppressWarnings({"ResultOfMethodCallIgnored"})
+ public static void visitFiles(final File pDirectory, final FileFilter pFilter, final Visitor pVisitor) {
+ Validate.notNull(pDirectory, "directory");
+ Validate.notNull(pVisitor, "visitor");
+
+ pDirectory.listFiles(new FileFilter() {
+ public boolean accept(final File pFile) {
+ if (pFilter == null || pFilter.accept(pFile)) {
+ pVisitor.visit(pFile);
+ }
+
+ return false;
+ }
+ });
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/FilenameMaskFilter.java b/common/common-io/src/main/java/com/twelvemonkeys/io/FilenameMaskFilter.java
index 6a541621..65e9e2de 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/FilenameMaskFilter.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/FilenameMaskFilter.java
@@ -1,242 +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 "TwelveMonkeys" 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 OWNER 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.
- *
- * The file name masks are used as a filter input and is given to the class via
- * the string array property:
- * {@code filenameMasksForInclusion} - Filename mask for exclusion of
- * files (default if both properties are defined)
- * {@code filenameMasksForExclusion} - Filename mask for exclusion of
- * files.
- *
- * 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 Eirik Torske
- * @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.
+ *
+ * The file name masks are used as a filter input and is given to the class via
+ * the string array property:
+ *
+ *
+ * - {@code filenameMasksForInclusion}
+ * - Filename mask for exclusion of
+ * files (default if both properties are defined).
+ * - {@code filenameMasksForExclusion}
+ * - Filename mask for exclusion of files.
+ *
+ *
+ * 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 Eirik Torske
+ * @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();
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/LittleEndianDataInputStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/LittleEndianDataInputStream.java
index 7eb31923..2cb94a03 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/LittleEndianDataInputStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/LittleEndianDataInputStream.java
@@ -1,451 +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 "TwelveMonkeys" 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 OWNER 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.
- *
- * The standard {@code java.io.DataInputStream} class
- * which this class imitates reads big endian quantities.
- *
- * 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.
- *
- *
- * @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
- * @see java.io.DataInputStream
- * @see java.io.DataInput
- * @see java.io.DataOutput
- *
- * @author Elliotte Rusty Harold
- * @author Harald Kuhr
- * @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 big 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}.
- *
- * 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}.
- *
- * 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}.
- *
- * 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}.
- *
- * 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.
+ *
+ * The standard {@code java.io.DataInputStream} class
+ * which this class imitates reads big endian quantities.
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
+ * @see java.io.DataInputStream
+ * @see java.io.DataInput
+ * @see java.io.DataOutput
+ *
+ * @author Elliotte Rusty Harold
+ * @author Harald Kuhr
+ * @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 big 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}.
+ *
+ * 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}.
+ *
+ * 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}.
+ *
+ * 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}.
+ *
+ * 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();
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/LittleEndianDataOutputStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/LittleEndianDataOutputStream.java
index 78699d73..68905fa7 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/LittleEndianDataOutputStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/LittleEndianDataOutputStream.java
@@ -1,339 +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 "TwelveMonkeys" 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 OWNER 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.
- *
- * The standard {@code java.io.DataOutputStream} class which this class
- * imitates uses big endian integers.
- *
- * 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.
- *
- *
- * @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 big 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.
+ *
+ * The standard {@code java.io.DataOutputStream} class which this class
+ * imitates uses big endian integers.
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * @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 big 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;
+ }
}
\ No newline at end of file
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/LittleEndianRandomAccessFile.java b/common/common-io/src/main/java/com/twelvemonkeys/io/LittleEndianRandomAccessFile.java
index 6cb701dc..98e54489 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/LittleEndianRandomAccessFile.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/LittleEndianRandomAccessFile.java
@@ -1,626 +1,628 @@
-/*
- * 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 "TwelveMonkeys" 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 OWNER 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.nio.channels.FileChannel;
-
-/**
- * A replacement for {@link java.io.RandomAccessFile} that is capable of reading
- * and writing data in little endian byte order.
- *
- * 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.
- *
- *
- * @see com.twelvemonkeys.io.LittleEndianDataInputStream
- * @see com.twelvemonkeys.io.LittleEndianDataOutputStream
- * @see java.io.RandomAccessFile
- * @see java.io.DataInput
- * @see java.io.DataOutput
- *
- * @author Elliotte Rusty Harold
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/LittleEndianRandomAccessFile.java#1 $
- */
-public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
- private RandomAccessFile file;
-
- public LittleEndianRandomAccessFile(final String pName, final String pMode) throws FileNotFoundException {
- this(FileUtil.resolve(pName), pMode);
- }
-
- public LittleEndianRandomAccessFile(final File pFile, final String pMode) throws FileNotFoundException {
- file = new RandomAccessFile(pFile, pMode);
- }
-
- public void close() throws IOException {
- file.close();
- }
-
- public FileChannel getChannel() {
- return file.getChannel();
- }
-
- public FileDescriptor getFD() throws IOException {
- return file.getFD();
- }
-
- public long getFilePointer() throws IOException {
- return file.getFilePointer();
- }
-
- public long length() throws IOException {
- return file.length();
- }
-
- public int read() throws IOException {
- return file.read();
- }
-
- public int read(final byte[] b) throws IOException {
- return file.read(b);
- }
-
- public int read(final byte[] b, final int off, final int len) throws IOException {
- return file.read(b, off, len);
- }
-
- public void readFully(final byte[] b) throws IOException {
- file.readFully(b);
- }
-
- public void readFully(final byte[] b, final int off, final int len) throws IOException {
- file.readFully(b, off, len);
- }
-
- public String readLine() throws IOException {
- return file.readLine();
- }
-
- /**
- * 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 = file.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 = file.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 = file.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 = file.read();
- int byte2 = file.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 = file.read();
- int byte2 = file.read();
-
- if (byte2 < 0) {
- throw new EOFException();
- }
-
- //return ((byte2 << 24) >> 16) + ((byte1 << 24) >> 24);
- 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 = file.read();
- int byte2 = file.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 = file.read();
- int byte2 = file.read();
- int byte3 = file.read();
- int byte4 = file.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 = file.read();
- long byte2 = file.read();
- long byte3 = file.read();
- long byte4 = file.read();
- long byte5 = file.read();
- long byte6 = file.read();
- long byte7 = file.read();
- long byte8 = file.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 big 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 = file.read();
- int byte2 = file.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());
- }
-
- /**
- * Sets the file-pointer offset, measured from the beginning of this
- * file, at which the next read or write occurs. The offset may be
- * set beyond the end of the file. Setting the offset beyond the end
- * of the file does not change the file length. The file length will
- * change only by writing after the offset has been set beyond the end
- * of the file.
- *
- * @param pos the offset position, measured in bytes from the
- * beginning of the file, at which to set the file
- * pointer.
- * @exception IOException if {@code pos} is less than
- * {@code 0} or if an I/O error occurs.
- */
- public void seek(final long pos) throws IOException {
- file.seek(pos);
- }
-
- public void setLength(final long newLength) throws IOException {
- file.setLength(newLength);
- }
-
- public int skipBytes(final int n) throws IOException {
- return file.skipBytes(n);
- }
-
- public void write(final byte[] b) throws IOException {
- file.write(b);
- }
-
- public void write(final byte[] b, final int off, final int len) throws IOException {
- file.write(b, off, len);
- }
-
- public void write(final int b) throws IOException {
- file.write(b);
- }
-
- /**
- * 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 {
- file.write(pByte);
- }
-
- /**
- * 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 {
- file.write(pShort & 0xFF);
- file.write((pShort >>> 8) & 0xFF);
- }
-
- /**
- * 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 {
- file.write(pChar & 0xFF);
- file.write((pChar >>> 8) & 0xFF);
- }
-
- /**
- * 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 {
- file.write(pInt & 0xFF);
- file.write((pInt >>> 8) & 0xFF);
- file.write((pInt >>> 16) & 0xFF);
- file.write((pInt >>> 24) & 0xFF);
- }
-
- /**
- * 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 {
- file.write((int) pLong & 0xFF);
- file.write((int) (pLong >>> 8) & 0xFF);
- file.write((int) (pLong >>> 16) & 0xFF);
- file.write((int) (pLong >>> 24) & 0xFF);
- file.write((int) (pLong >>> 32) & 0xFF);
- file.write((int) (pLong >>> 40) & 0xFF);
- file.write((int) (pLong >>> 48) & 0xFF);
- file.write((int) (pLong >>> 56) & 0xFF);
- }
-
- /**
- * 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 {@code writeByte()} method.
- *
- * @param pString the {@code String} value to be written.
- * @throws IOException if the underlying stream throws an IOException.
- * @see #writeByte(int)
- * @see #file
- */
- public void writeBytes(String pString) throws IOException {
- int length = pString.length();
-
- for (int i = 0; i < length; i++) {
- file.write((byte) pString.charAt(i));
- }
- }
-
- /**
- * 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 #file
- */
- public void writeChars(String pString) throws IOException {
- int length = pString.length();
-
- for (int i = 0; i < length; i++) {
- int c = pString.charAt(i);
- file.write(c & 0xFF);
- file.write((c >>> 8) & 0xFF);
- }
- }
-
- /**
- * 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 big 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();
- }
-
- file.write((numbytes >>> 8) & 0xFF);
- file.write(numbytes & 0xFF);
-
- for (int i = 0; i < numchars; i++) {
- int c = pString.charAt(i);
-
- if ((c >= 0x0001) && (c <= 0x007F)) {
- file.write(c);
- }
- else if (c > 0x07FF) {
- file.write(0xE0 | ((c >> 12) & 0x0F));
- file.write(0x80 | ((c >> 6) & 0x3F));
- file.write(0x80 | (c & 0x3F));
- }
- else {
- file.write(0xC0 | ((c >> 6) & 0x1F));
- file.write(0x80 | (c & 0x3F));
- }
- }
- }
-}
+/*
+ * 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.nio.channels.FileChannel;
+
+/**
+ * A replacement for {@link java.io.RandomAccessFile} that is capable of reading
+ * and writing data in little endian byte order.
+ *
+ * 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.
+ *
+ *
+ *
+ * @see com.twelvemonkeys.io.LittleEndianDataInputStream
+ * @see com.twelvemonkeys.io.LittleEndianDataOutputStream
+ * @see java.io.RandomAccessFile
+ * @see java.io.DataInput
+ * @see java.io.DataOutput
+ *
+ * @author Elliotte Rusty Harold
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/LittleEndianRandomAccessFile.java#1 $
+ */
+public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
+ private RandomAccessFile file;
+
+ public LittleEndianRandomAccessFile(final String pName, final String pMode) throws FileNotFoundException {
+ this(FileUtil.resolve(pName), pMode);
+ }
+
+ public LittleEndianRandomAccessFile(final File pFile, final String pMode) throws FileNotFoundException {
+ file = new RandomAccessFile(pFile, pMode);
+ }
+
+ public void close() throws IOException {
+ file.close();
+ }
+
+ public FileChannel getChannel() {
+ return file.getChannel();
+ }
+
+ public FileDescriptor getFD() throws IOException {
+ return file.getFD();
+ }
+
+ public long getFilePointer() throws IOException {
+ return file.getFilePointer();
+ }
+
+ public long length() throws IOException {
+ return file.length();
+ }
+
+ public int read() throws IOException {
+ return file.read();
+ }
+
+ public int read(final byte[] b) throws IOException {
+ return file.read(b);
+ }
+
+ public int read(final byte[] b, final int off, final int len) throws IOException {
+ return file.read(b, off, len);
+ }
+
+ public void readFully(final byte[] b) throws IOException {
+ file.readFully(b);
+ }
+
+ public void readFully(final byte[] b, final int off, final int len) throws IOException {
+ file.readFully(b, off, len);
+ }
+
+ public String readLine() throws IOException {
+ return file.readLine();
+ }
+
+ /**
+ * 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 = file.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 = file.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 = file.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 = file.read();
+ int byte2 = file.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 = file.read();
+ int byte2 = file.read();
+
+ if (byte2 < 0) {
+ throw new EOFException();
+ }
+
+ //return ((byte2 << 24) >> 16) + ((byte1 << 24) >> 24);
+ 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 = file.read();
+ int byte2 = file.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 = file.read();
+ int byte2 = file.read();
+ int byte3 = file.read();
+ int byte4 = file.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 = file.read();
+ long byte2 = file.read();
+ long byte3 = file.read();
+ long byte4 = file.read();
+ long byte5 = file.read();
+ long byte6 = file.read();
+ long byte7 = file.read();
+ long byte8 = file.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 big 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 = file.read();
+ int byte2 = file.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());
+ }
+
+ /**
+ * Sets the file-pointer offset, measured from the beginning of this
+ * file, at which the next read or write occurs. The offset may be
+ * set beyond the end of the file. Setting the offset beyond the end
+ * of the file does not change the file length. The file length will
+ * change only by writing after the offset has been set beyond the end
+ * of the file.
+ *
+ * @param pos the offset position, measured in bytes from the
+ * beginning of the file, at which to set the file
+ * pointer.
+ * @exception IOException if {@code pos} is less than
+ * {@code 0} or if an I/O error occurs.
+ */
+ public void seek(final long pos) throws IOException {
+ file.seek(pos);
+ }
+
+ public void setLength(final long newLength) throws IOException {
+ file.setLength(newLength);
+ }
+
+ public int skipBytes(final int n) throws IOException {
+ return file.skipBytes(n);
+ }
+
+ public void write(final byte[] b) throws IOException {
+ file.write(b);
+ }
+
+ public void write(final byte[] b, final int off, final int len) throws IOException {
+ file.write(b, off, len);
+ }
+
+ public void write(final int b) throws IOException {
+ file.write(b);
+ }
+
+ /**
+ * 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 {
+ file.write(pByte);
+ }
+
+ /**
+ * 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 {
+ file.write(pShort & 0xFF);
+ file.write((pShort >>> 8) & 0xFF);
+ }
+
+ /**
+ * 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 {
+ file.write(pChar & 0xFF);
+ file.write((pChar >>> 8) & 0xFF);
+ }
+
+ /**
+ * 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 {
+ file.write(pInt & 0xFF);
+ file.write((pInt >>> 8) & 0xFF);
+ file.write((pInt >>> 16) & 0xFF);
+ file.write((pInt >>> 24) & 0xFF);
+ }
+
+ /**
+ * 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 {
+ file.write((int) pLong & 0xFF);
+ file.write((int) (pLong >>> 8) & 0xFF);
+ file.write((int) (pLong >>> 16) & 0xFF);
+ file.write((int) (pLong >>> 24) & 0xFF);
+ file.write((int) (pLong >>> 32) & 0xFF);
+ file.write((int) (pLong >>> 40) & 0xFF);
+ file.write((int) (pLong >>> 48) & 0xFF);
+ file.write((int) (pLong >>> 56) & 0xFF);
+ }
+
+ /**
+ * 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 {@code writeByte()} method.
+ *
+ * @param pString the {@code String} value to be written.
+ * @throws IOException if the underlying stream throws an IOException.
+ * @see #writeByte(int)
+ * @see #file
+ */
+ public void writeBytes(String pString) throws IOException {
+ int length = pString.length();
+
+ for (int i = 0; i < length; i++) {
+ file.write((byte) pString.charAt(i));
+ }
+ }
+
+ /**
+ * 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 #file
+ */
+ public void writeChars(String pString) throws IOException {
+ int length = pString.length();
+
+ for (int i = 0; i < length; i++) {
+ int c = pString.charAt(i);
+ file.write(c & 0xFF);
+ file.write((c >>> 8) & 0xFF);
+ }
+ }
+
+ /**
+ * 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 big 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();
+ }
+
+ file.write((numbytes >>> 8) & 0xFF);
+ file.write(numbytes & 0xFF);
+
+ for (int i = 0; i < numchars; i++) {
+ int c = pString.charAt(i);
+
+ if ((c >= 0x0001) && (c <= 0x007F)) {
+ file.write(c);
+ }
+ else if (c > 0x07FF) {
+ file.write(0xE0 | ((c >> 12) & 0x0F));
+ file.write(0x80 | ((c >> 6) & 0x3F));
+ file.write(0x80 | (c & 0x3F));
+ }
+ else {
+ file.write(0xC0 | ((c >> 6) & 0x1F));
+ file.write(0x80 | (c & 0x3F));
+ }
+ }
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/MemoryCacheSeekableStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/MemoryCacheSeekableStream.java
index 70bf86ff..3a8d89d3 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/MemoryCacheSeekableStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/MemoryCacheSeekableStream.java
@@ -1,191 +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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @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 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;
- }
-
- 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 Harald Kuhr
+ * @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 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;
+ }
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/NullInputStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/NullInputStream.java
index aa27ac30..02e45387 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/NullInputStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/NullInputStream.java
@@ -1,80 +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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @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 Harald Kuhr
+ * @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;
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/NullOutputStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/NullOutputStream.java
index ad99120b..b584631a 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/NullOutputStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/NullOutputStream.java
@@ -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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @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 Harald Kuhr
+ * @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 {
+ }
}
\ No newline at end of file
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/RandomAccessStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/RandomAccessStream.java
index 196da6e9..e3e5051d 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/RandomAccessStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/RandomAccessStream.java
@@ -1,239 +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 "TwelveMonkeys" 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 OWNER 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.IOException;
-import java.io.EOFException;
-
-/**
- * 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 Harald Kuhr
- * @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.
- *
- * Note that read access is NOT synchronized.
- *
- * @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.
- *
- * Note that write access is NOT synchronized.
- *
- * @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 Harald Kuhr
+ * @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.
+ *
+ * Note that read access is NOT synchronized.
+ *
+ *
+ * @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.
+ *
+ * Note that write access is NOT synchronized.
+ *
+ *
+ * @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);
+ }
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/Seekable.java b/common/common-io/src/main/java/com/twelvemonkeys/io/Seekable.java
index b84d5d3e..204d1e10 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/Seekable.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/Seekable.java
@@ -1,184 +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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @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.
- *
- * An {@code IndexOutOfBoundsException} will be thrown if pPosition is smaller
- * than the flushed position (as returned by {@link #getFlushedPosition()}).
- *
- * 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.
- *
- * 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.
- *
- * Calls to reset without a corresponding call to mark will either:
- *
- * - throw an {@code IOException}
- * - or, reset to the beginning of the stream.
- *
- * 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}.
- *
- * 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 Harald Kuhr
+ * @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.
+ *
+ * An {@code IndexOutOfBoundsException} will be thrown if pPosition is smaller
+ * than the flushed position (as returned by {@link #getFlushedPosition()}).
+ *
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * Calls to reset without a corresponding call to mark will either:
+ *
+ *
+ * - throw an {@code IOException}
+ * - or, reset to the beginning of the stream.
+ *
+ *
+ * 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}.
+ *
+ * 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;
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/SeekableInputStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/SeekableInputStream.java
index 0a605174..f585ebea 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/SeekableInputStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/SeekableInputStream.java
@@ -1,236 +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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @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 markedPositions = new Stack();
-
- /// 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 Harald Kuhr
+ * @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 markedPositions = new Stack();
+
+ /// 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();
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/SeekableOutputStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/SeekableOutputStream.java
index 133597ec..bdce032b 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/SeekableOutputStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/SeekableOutputStream.java
@@ -1,138 +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 "TwelveMonkeys" 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 OWNER 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.OutputStream;
-import java.io.IOException;
-import java.util.Stack;
-
-/**
- * Abstract base class for {@code OutputStream}s implementing the
- * {@code Seekable} interface.
- *
- * @see SeekableInputStream
- *
- * @author Harald Kuhr
- * @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 markedPositions = new Stack();
-
- /// 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 Harald Kuhr
+ * @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 markedPositions = new Stack();
+
+ /// 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;
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/StringArrayReader.java b/common/common-io/src/main/java/com/twelvemonkeys/io/StringArrayReader.java
index d90b3e1b..45ec3f9b 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/StringArrayReader.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/StringArrayReader.java
@@ -1,187 +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 "TwelveMonkeys" 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 OWNER 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.StringReader;
-import java.io.IOException;
-import java.io.Reader;
-
-/**
- * StringArrayReader
- *
- *
- * @author Harald Kuhr
- * @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 Harald Kuhr
+ * @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;
+ }
+ }
+
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/SubStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/SubStream.java
index d45d6b78..76a3c01f 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/SubStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/SubStream.java
@@ -1,135 +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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @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 not 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 Harald Kuhr
+ * @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 not 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;
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/UnixFileSystem.java b/common/common-io/src/main/java/com/twelvemonkeys/io/UnixFileSystem.java
index 3675fee5..55caa5e9 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/UnixFileSystem.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/UnixFileSystem.java
@@ -1,105 +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 "TwelveMonkeys" 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 OWNER 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.File;
-import java.io.IOException;
-import java.io.BufferedReader;
-
-/**
- * UnixFileSystem
- *
- *
- * @author Harald Kuhr
- * @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 Harald Kuhr
+ * @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";
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/Win32File.java b/common/common-io/src/main/java/com/twelvemonkeys/io/Win32File.java
index 872ad480..fa6c82ef 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/Win32File.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/Win32File.java
@@ -1,190 +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 "TwelveMonkeys" 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 OWNER 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.*;
-
-/**
- * Win32File
- *
- *
- * @author Harald Kuhr
- * @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 Harald Kuhr
+ * @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));
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/Win32FileSystem.java b/common/common-io/src/main/java/com/twelvemonkeys/io/Win32FileSystem.java
index 8e8ff5a9..ae3d3608 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/Win32FileSystem.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/Win32FileSystem.java
@@ -1,90 +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 "TwelveMonkeys" 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 OWNER 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.BufferedReader;
-import java.io.IOException;
-
-/**
- * WindowsFileSystem
- *
- *
- * @author Harald Kuhr
- * @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 Harald Kuhr
+ * @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";
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/Win32Lnk.java b/common/common-io/src/main/java/com/twelvemonkeys/io/Win32Lnk.java
index 738243e1..34eb0be6 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/Win32Lnk.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/Win32Lnk.java
@@ -1,473 +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 "TwelveMonkeys" 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 OWNER 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.
- *
- * This class is based on example code from
- * Swing Hacks,
- * By Joshua Marinacci, Chris Adamson (O'Reilly, ISBN: 0-596-00907-0), Hack 30.
- *
- * @author Harald Kuhr
- * @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.
- *
- * 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.
+ *
+ * This class is based on example code from
+ * Swing Hacks,
+ * By Joshua Marinacci, Chris Adamson (O'Reilly, ISBN: 0-596-00907-0), Hack 30.
+ *
+ *
+ * @author Harald Kuhr
+ * @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.
+ *
+ * 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();
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/WriterOutputStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/WriterOutputStream.java
index d3c0a1a7..ff0893ad 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/WriterOutputStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/WriterOutputStream.java
@@ -1,237 +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 "TwelveMonkeys" 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 OWNER 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.charset.Charset;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-
-/**
- * Wraps a {@code Writer} in an {@code OutputStream}.
- *
- * Instances of this class are not thread-safe.
- *
- * 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.
- *
- *
- *
- * @author Harald Kuhr
- * @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 = "������ 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}.
+ *
+ * Instances of this class are not thread-safe.
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * @author Harald Kuhr
+ * @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 = "������ 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);
+ }
+ }
}
\ No newline at end of file
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Base64Decoder.java b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Base64Decoder.java
index 7e385c3a..2660b2f7 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Base64Decoder.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Base64Decoder.java
@@ -1,186 +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 "TwelveMonkeys" 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 OWNER 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 RFC 1421
- * @see
- *
- * @see Base64Encoder
- *
- * @author Harald Kuhr
- * @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 RFC 1421
+ * @see RFC 2045
+ *
+ * @see Base64Encoder
+ *
+ * @author Harald Kuhr
+ * @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();
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Base64Encoder.java b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Base64Encoder.java
index d8336498..8a9eaa5f 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Base64Encoder.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Base64Encoder.java
@@ -1,104 +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 "TwelveMonkeys" 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 OWNER 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.OutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * {@code Encoder} implementation for standard base64 encoding.
- *
- * @see RFC 1421
- * @see
- *
- * @see Base64Decoder
- *
- * @author Harald Kuhr
- * @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 RFC 1421
+ * @see RFC 2045
+ *
+ * @see Base64Decoder
+ *
+ * @author Harald Kuhr
+ * @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;
+ }
+ }
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/DecodeException.java b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/DecodeException.java
index 7acf88af..c377d2d7 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/DecodeException.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/DecodeException.java
@@ -1,54 +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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @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 Harald Kuhr
+ * @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);
+ }
}
\ No newline at end of file
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Decoder.java b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Decoder.java
index aa7576d0..eae19df0 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Decoder.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Decoder.java
@@ -1,66 +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 "TwelveMonkeys" 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 OWNER 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}.
- *
- * Important note: Decoder implementations are typically not synchronized.
- *
- * @see Encoder
- * @see DecoderStream
- *
- * @author Harald Kuhr
- * @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}.
+ *
+ * Important note: Decoder implementations are typically not synchronized.
+ *
+ *
+ * @see Encoder
+ * @see DecoderStream
+ *
+ * @author Harald Kuhr
+ * @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;
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java
index 07826694..ec542a46 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java
@@ -1,198 +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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @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 Harald Kuhr
+ * @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;
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Encoder.java b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Encoder.java
index 1864e4dc..c5c8278c 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Encoder.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Encoder.java
@@ -1,63 +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 "TwelveMonkeys" 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 OWNER 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}.
- *
- * Important note: Encoder implementations are typically not synchronized.
- *
- * @see Decoder
- * @see EncoderStream
- *
- * @author Harald Kuhr
- * @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}.
+ *
+ * Important note: Encoder implementations are typically not synchronized.
+ *
+ *
+ * @see Decoder
+ * @see EncoderStream
+ *
+ * @author Harald Kuhr
+ * @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()?
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java
index f1ba67ef..dafceb2e 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java
@@ -1,135 +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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @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 Harald Kuhr
+ * @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);
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java
index 86c32a1f..69865e39 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java
@@ -1,208 +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 "TwelveMonkeys" 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 OWNER 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.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-
-/**
- * Decoder implementation for Apple PackBits run-length encoding.
- *
- * From Wikipedia, the free encyclopedia
- * PackBits is a fast, simple compression scheme for run-length encoding of
- * data.
- *
- * 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.
- *
- * 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).
- *
- * Header byte | Data |
- * 0 to 127 | 1 + n literal bytes of data |
- * 0 to -127 | One byte of data, repeated 1 - n times in
- * the decompressed output |
- * -128 | No operation |
- *
- * 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.
- *
- * See Understanding PackBits
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java#1 $
- */
-public final class PackBitsDecoder implements Decoder {
- // TODO: Look at ICNSImageReader#unpackbits... What is this weirdness?
-
- private final boolean disableNoOp;
- private final byte[] sample;
-
- private int leftOfRun;
- private boolean splitRun;
- private boolean reachedEOF;
-
- /** Creates a {@code PackBitsDecoder}. */
- public PackBitsDecoder() {
- this(1, false);
- }
-
- /**
- * Creates a {@code PackBitsDecoder}, with optional compatibility mode.
- *
- * 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.
- *
- * @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
- */
- public PackBitsDecoder(final boolean disableNoOp) {
- this(1, disableNoOp);
- }
-
- /**
- * Creates a {@code PackBitsDecoder}, with optional compatibility mode.
- *
- * 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.
- *
- * @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
- */
- public PackBitsDecoder(int sampleSize, final boolean disableNoOp) {
- this.sample = new byte[sampleSize];
- this.disableNoOp = disableNoOp;
- }
-
- /**
- * Decodes bytes from the given input stream, to the given buffer.
- *
- * @param stream the stream to decode from
- * @param buffer a byte array, minimum 128 (or 129 if no-op is disabled) bytes long
- * @return The number of bytes decoded
- *
- * @throws java.io.IOException
- */
- public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
- if (reachedEOF) {
- return -1;
- }
-
- // TODO: Don't decode more than single runs, because some writers add pad bytes inside the stream...
- while (buffer.hasRemaining()) {
- int n;
-
- if (splitRun) {
- // Continue run
- n = leftOfRun;
- splitRun = false;
- }
- else {
- // Start new run
- int b = stream.read();
- if (b < 0) {
- reachedEOF = true;
- break;
- }
- n = (byte) b;
- }
-
- // Split run at or before max
- if (n >= 0 && n + 1 > buffer.remaining()) {
- leftOfRun = n;
- splitRun = true;
- break;
- }
- else if (n < 0 && -n + 1 > buffer.remaining()) {
- leftOfRun = n;
- splitRun = true;
- break;
- }
-
- try {
- if (n >= 0) {
- // Copy next n + 1 bytes literally
- readFully(stream, buffer, sample.length * (n + 1));
- }
- // Allow -128 for compatibility, see above
- else if (disableNoOp || n != -128) {
- // Replicate the next byte -n + 1 times
- for (int s = 0; s < sample.length; s++) {
- sample[s] = readByte(stream);
- }
-
- for (int i = -n + 1; i > 0; i--) {
- buffer.put(sample);
- }
- }
- // else NOOP (-128)
- }
- catch (IndexOutOfBoundsException e) {
- throw new DecodeException("Error in PackBits decompression, data seems corrupt", e);
- }
- }
-
- return buffer.position();
- }
-
- static byte readByte(final InputStream pStream) throws IOException {
- int read = pStream.read();
-
- if (read < 0) {
- throw new EOFException("Unexpected end of PackBits stream");
- }
-
- return (byte) read;
- }
-
- static void readFully(final InputStream pStream, final ByteBuffer pBuffer, final int pLength) throws IOException {
- if (pLength < 0) {
- throw new IndexOutOfBoundsException(String.format("Negative length: %d", pLength));
- }
-
- int total = 0;
-
- while (total < pLength) {
- int count = pStream.read(pBuffer.array(), pBuffer.arrayOffset() + pBuffer.position() + total, pLength - total);
-
- if (count < 0) {
- throw new EOFException("Unexpected end of PackBits stream");
- }
-
- total += count;
- }
-
- pBuffer.position(pBuffer.position() + total);
- }
-}
+/*
+ * 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.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * Decoder implementation for Apple PackBits run-length encoding.
+ *
+ * From Wikipedia, the free encyclopedia
+ *
+ * PackBits is a fast, simple compression scheme for run-length encoding of
+ * data.
+ *
+ *
+ * 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.
+ *
+ *
+ * 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).
+ *
+ *
+ * PackBits
+ * Header byte | Data |
+ * 0 to 127 | 1 + n literal bytes of data |
+ * 0 to -127 | One byte of data, repeated 1 - n times in the decompressed output |
+ * -128 | No operation |
+ *
+ *
+ * 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.
+ *
+ *
+ * @see Understanding PackBits
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java#1 $
+ */
+public final class PackBitsDecoder implements Decoder {
+ // TODO: Look at ICNSImageReader#unpackbits... What is this weirdness?
+
+ private final boolean disableNoOp;
+ private final byte[] sample;
+
+ private boolean reachedEOF;
+
+ /** Creates a {@code PackBitsDecoder}. */
+ public PackBitsDecoder() {
+ this(1, false);
+ }
+
+ /**
+ * Creates a {@code PackBitsDecoder}, with optional compatibility mode.
+ *
+ * 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.
+ *
+ *
+ * @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
+ */
+ public PackBitsDecoder(final boolean disableNoOp) {
+ this(1, disableNoOp);
+ }
+
+ /**
+ * Creates a {@code PackBitsDecoder}, with optional compatibility mode.
+ *
+ * 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.
+ *
+ *
+ * @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
+ */
+ public PackBitsDecoder(int sampleSize, final boolean disableNoOp) {
+ this.sample = new byte[sampleSize];
+ this.disableNoOp = disableNoOp;
+ }
+
+ /**
+ * Decodes bytes from the given input stream, to the given buffer.
+ *
+ * @param stream the stream to decode from
+ * @param buffer a byte array, minimum 128 (or 129 if no-op is disabled) bytes long
+ * @return The number of bytes decoded
+ *
+ * @throws java.io.IOException if a problem occurs during decoding.
+ */
+ public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
+ if (reachedEOF) {
+ return -1;
+ }
+
+ // NOTE: We don't decode more than single runs, because some writers add pad bytes inside the stream...
+ // Start new run
+ int b = stream.read();
+ if (b < 0) {
+ reachedEOF = true;
+ return 0;
+ }
+
+ int n = (byte) b;
+
+ try {
+ if (n >= 0) {
+ // Copy next n + 1 bytes literally
+ readFully(stream, buffer, sample.length * (n + 1));
+ }
+ // Allow -128 for compatibility, see above
+ else if (disableNoOp || n != -128) {
+ // Replicate the next byte -n + 1 times
+ for (int s = 0; s < sample.length; s++) {
+ sample[s] = readByte(stream);
+ }
+
+ for (int i = -n + 1; i > 0; i--) {
+ buffer.put(sample);
+ }
+ }
+ // else NOOP (-128)
+ }
+ catch (IndexOutOfBoundsException e) {
+ throw new DecodeException("Error in PackBits decompression, data seems corrupt", e);
+ }
+
+ return buffer.position();
+ }
+
+ static byte readByte(final InputStream pStream) throws IOException {
+ int read = pStream.read();
+
+ if (read < 0) {
+ throw new EOFException("Unexpected end of PackBits stream");
+ }
+
+ return (byte) read;
+ }
+
+ static void readFully(final InputStream pStream, final ByteBuffer pBuffer, final int pLength) throws IOException {
+ if (pLength < 0) {
+ throw new IndexOutOfBoundsException(String.format("Negative length: %d", pLength));
+ }
+
+ int total = 0;
+
+ while (total < pLength) {
+ int count = pStream.read(pBuffer.array(), pBuffer.arrayOffset() + pBuffer.position() + total, pLength - total);
+
+ if (count < 0) {
+ throw new EOFException("Unexpected end of PackBits stream");
+ }
+
+ total += count;
+ }
+
+ pBuffer.position(pBuffer.position() + total);
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/PackBitsEncoder.java b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/PackBitsEncoder.java
index e51a94e8..4bbfc15f 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/PackBitsEncoder.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/PackBitsEncoder.java
@@ -1,130 +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 "TwelveMonkeys" 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 OWNER 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.OutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * Encoder implementation for Apple PackBits run-length encoding.
- *
- * From Wikipedia, the free encyclopedia
- * PackBits is a fast, simple compression scheme for run-length encoding of
- * data.
- *
- * 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.
- *
- * 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).
- *
- * Header byte | Data |
- * 0 to 127 | 1 + n literal bytes of data |
- * 0 to -127 | One byte of data, repeated 1 - n times in
- * the decompressed output |
- * -128 | No operation |
- *
- * 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.
- *
- * See Understanding PackBits
- *
- * @author Harald Kuhr
- * @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.
+ *
+ * From Wikipedia, the free encyclopedia
+ *
+ * PackBits is a fast, simple compression scheme for run-length encoding of
+ * data.
+ *
+ *
+ * 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.
+ *
+ *
+ * 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).
+ *
+ *
+ * PackBits
+ * Header byte | Data |
+ * 0 to 127 | 1 + n literal bytes of data |
+ * 0 to -127 | One byte of data, repeated 1 - n times in the decompressed output |
+ * -128 | No operation |
+ *
+ *
+ * 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.
+ *
+ *
+ * @see Understanding PackBits
+ *
+ * @author Harald Kuhr
+ * @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++]);
+ }
+ }
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/package-info.java b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/package-info.java
index 72d47b16..d7ff80c0 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/package-info.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/package-info.java
@@ -1,3 +1,33 @@
+/*
+ * 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.
+ */
+
/**
* Contains customized stream classes, that can read or write compressed data on the fly,
* along with encoders and decoders for popular stream formats, such as Base64, ZIP (deflate), LZW, PackBits etc..
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/CompoundDocument.java b/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/CompoundDocument.java
index 8dcffcc9..75aeb477 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/CompoundDocument.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/CompoundDocument.java
@@ -1,764 +1,801 @@
-/*
- * 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 "TwelveMonkeys" 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 OWNER 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.*;
-import com.twelvemonkeys.lang.StringUtil;
-
-import javax.imageio.stream.ImageInputStream;
-import java.io.*;
-import java.util.Arrays;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.UUID;
-
-/**
- * Represents a read-only OLE2 compound document.
- *
- *
- * NOTE: This class is not synchronized. Accessing the document or its
- * entries from different threads, will need synchronization on the document
- * instance.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/ole2/CompoundDocument.java#4 $
- */
-public final class CompoundDocument {
- // TODO: Write support...
- // TODO: Properties: http://support.microsoft.com/kb/186898
-
- static final byte[] MAGIC = new byte[]{
- (byte) 0xD0, (byte) 0xCF, (byte) 0x11, (byte) 0xE0,
- (byte) 0xA1, (byte) 0xB1, (byte) 0x1A, (byte) 0xE1,
- };
-
- private static final int FREE_SID = -1;
- private static final int END_OF_CHAIN_SID = -2;
- private static final int SAT_SECTOR_SID = -3; // Sector used by SAT
- private static final int MSAT_SECTOR_SID = -4; // Sector used my Master SAT
-
- public static final int HEADER_SIZE = 512;
-
- /** The epoch offset of CompoundDocument time stamps */
- public final static long EPOCH_OFFSET = -11644477200000L;
-
- private final DataInput input;
-
- private UUID uUID;
-
- private int sectorSize;
- private int shortSectorSize;
-
- private int directorySId;
-
- private int minStreamSize;
-
- private int shortSATSId;
- private int shortSATSize;
-
- // Master Sector Allocation Table
- private int[] masterSAT;
- private int[] SAT;
- private int[] shortSAT;
-
- private Entry rootEntry;
- private SIdChain shortStreamSIdChain;
- private SIdChain directorySIdChain;
-
- /**
- * Creates a (for now) read only {@code CompoundDocument}.
- *
- * @param pFile the file to read from
- *
- * @throws IOException if an I/O exception occurs while reading the header
- */
- public CompoundDocument(final File pFile) throws IOException {
- input = new LittleEndianRandomAccessFile(FileUtil.resolve(pFile), "r");
-
- // TODO: Might be better to read header on first read operation?!
- // OTOH: It's also good to be fail-fast, so at least we should make
- // sure we're reading a valid document
- readHeader();
- }
-
- /**
- * Creates a read only {@code CompoundDocument}.
- *
- * @param pInput the input to read from
- *
- * @throws IOException if an I/O exception occurs while reading the header
- */
- public CompoundDocument(final InputStream pInput) throws IOException {
- this(new FileCacheSeekableStream(pInput));
- }
-
- // For testing only, consider exposing later
- CompoundDocument(final SeekableInputStream pInput) throws IOException {
- input = new SeekableLittleEndianDataInputStream(pInput);
-
- // TODO: Might be better to read header on first read operation?!
- // OTOH: It's also good to be fail-fast, so at least we should make
- // sure we're reading a valid document
- readHeader();
- }
-
- /**
- * Creates a read only {@code CompoundDocument}.
- *
- * @param pInput the input to read from
- *
- * @throws IOException if an I/O exception occurs while reading the header
- */
- public CompoundDocument(final ImageInputStream pInput) throws IOException {
- input = pInput;
-
- // TODO: Might be better to read header on first read operation?!
- // OTOH: It's also good to be fail-fast, so at least we should make
- // sure we're reading a valid document
- readHeader();
- }
-
- public static boolean canRead(final DataInput pInput) {
- return canRead(pInput, true);
- }
-
- // TODO: Refactor.. Figure out what we really need to expose to ImageIO for
- // easy reading of the Thumbs.db file
- // It's probably safer to create one version for InputStream and one for File
- private static boolean canRead(final DataInput pInput, final boolean pReset) {
- long pos = FREE_SID;
- if (pReset) {
- try {
- if (pInput instanceof InputStream && ((InputStream) pInput).markSupported()) {
- ((InputStream) pInput).mark(8);
- }
- else if (pInput instanceof ImageInputStream) {
- ((ImageInputStream) pInput).mark();
- }
- else if (pInput instanceof RandomAccessFile) {
- pos = ((RandomAccessFile) pInput).getFilePointer();
- }
- else if (pInput instanceof LittleEndianRandomAccessFile) {
- pos = ((LittleEndianRandomAccessFile) pInput).getFilePointer();
- }
- else {
- return false;
- }
- }
- catch (IOException ignore) {
- return false;
- }
- }
-
- try {
- byte[] magic = new byte[8];
- pInput.readFully(magic);
- return Arrays.equals(magic, MAGIC);
- }
- catch (IOException ignore) {
- // Ignore
- }
- finally {
- if (pReset) {
- try {
- if (pInput instanceof InputStream && ((InputStream) pInput).markSupported()) {
- ((InputStream) pInput).reset();
- }
- else if (pInput instanceof ImageInputStream) {
- ((ImageInputStream) pInput).reset();
- }
- else if (pInput instanceof RandomAccessFile) {
- ((RandomAccessFile) pInput).seek(pos);
- }
- else if (pInput instanceof LittleEndianRandomAccessFile) {
- ((LittleEndianRandomAccessFile) pInput).seek(pos);
- }
- }
- catch (IOException e) {
- // TODO: This isn't actually good enough...
- // Means something fucked up, and will fail...
- e.printStackTrace();
- }
- }
- }
-
- return false;
- }
-
- private void readHeader() throws IOException {
- if (masterSAT != null) {
- return;
- }
-
- if (!canRead(input, false)) {
- throw new CorruptDocumentException("Not an OLE 2 Compound Document");
- }
-
- // UID (seems to be all 0s)
- uUID = new UUID(input.readLong(), input.readLong());
-// System.out.println("uUID: " + uUID);
-
- // int version =
- input.readUnsignedShort();
-// System.out.println("version: " + version);
- // int revision =
- input.readUnsignedShort();
-// System.out.println("revision: " + revision);
-
- int byteOrder = input.readUnsignedShort();
-// System.out.printf("byteOrder: 0x%04x\n", byteOrder);
- if (byteOrder == 0xffff) {
- throw new CorruptDocumentException("Cannot read big endian OLE 2 Compound Documents");
- }
- else if (byteOrder != 0xfffe) {
- // Reversed, as I'm already reading little-endian
- throw new CorruptDocumentException(String.format("Unknown byte order marker: 0x%04x, expected 0xfffe or 0xffff", byteOrder));
- }
-
- sectorSize = 1 << input.readUnsignedShort();
-// System.out.println("sectorSize: " + sectorSize + " bytes");
- shortSectorSize = 1 << input.readUnsignedShort();
-// System.out.println("shortSectorSize: " + shortSectorSize + " bytes");
-
- // Reserved
- if (skipBytesFully(10) != 10) {
- throw new CorruptDocumentException();
- }
-
- int SATSize = input.readInt();
-// System.out.println("normalSATSize: " + SATSize);
-
- directorySId = input.readInt();
-// System.out.println("directorySId: " + directorySId);
-
- // Reserved
- if (skipBytesFully(4) != 4) {
- throw new CorruptDocumentException();
- }
-
- minStreamSize = input.readInt();
-// System.out.println("minStreamSize: " + minStreamSize + " bytes");
-
- shortSATSId = input.readInt();
-// System.out.println("shortSATSId: " + shortSATSId);
- shortSATSize = input.readInt();
-// System.out.println("shortSATSize: " + shortSATSize);
- int masterSATSId = input.readInt();
-// System.out.println("masterSATSId: " + masterSATSId);
- int masterSATSize = input.readInt();
-// System.out.println("masterSATSize: " + masterSATSize);
-
- // Read masterSAT: 436 bytes, containing up to 109 SIDs
- //System.out.println("MSAT:");
- masterSAT = new int[SATSize];
- final int headerSIds = Math.min(SATSize, 109);
- for (int i = 0; i < headerSIds; i++) {
- masterSAT[i] = input.readInt();
- //System.out.println("\tSID(" + i + "): " + masterSAT[i]);
- }
-
- if (masterSATSId == END_OF_CHAIN_SID) {
- // End of chain
- int freeSIdLength = 436 - (SATSize * 4);
- if (skipBytesFully(freeSIdLength) != freeSIdLength) {
- throw new CorruptDocumentException();
- }
- }
- else {
- // Parse the SIDs in the extended MasterSAT sectors...
- seekToSId(masterSATSId, FREE_SID);
-
- int index = headerSIds;
- for (int i = 0; i < masterSATSize; i++) {
- for (int j = 0; j < 127; j++) {
- int sid = input.readInt();
- switch (sid) {
- case FREE_SID:// Free
- break;
- default:
- masterSAT[index++] = sid;
- break;
- }
- }
-
- int next = input.readInt();
- if (next == END_OF_CHAIN_SID) {// End of chain
- break;
- }
-
- seekToSId(next, FREE_SID);
- }
- }
- }
-
- private int skipBytesFully(final int n) throws IOException {
- int toSkip = n;
-
- while (toSkip > 0) {
- int skipped = input.skipBytes(n);
- if (skipped <= 0) {
- break;
- }
-
- toSkip -= skipped;
- }
-
- return n - toSkip;
- }
-
- private void readSAT() throws IOException {
- if (SAT != null) {
- return;
- }
-
- final int intsPerSector = sectorSize / 4;
-
- // Read the Sector Allocation Table
- SAT = new int[masterSAT.length * intsPerSector];
-
- for (int i = 0; i < masterSAT.length; i++) {
- seekToSId(masterSAT[i], FREE_SID);
-
- for (int j = 0; j < intsPerSector; j++) {
- int nextSID = input.readInt();
- int index = (j + (i * intsPerSector));
-
- SAT[index] = nextSID;
- }
- }
-
- // Read the short-stream Sector Allocation Table
- SIdChain chain = getSIdChain(shortSATSId, FREE_SID);
- shortSAT = new int[shortSATSize * intsPerSector];
- for (int i = 0; i < shortSATSize; i++) {
- seekToSId(chain.get(i), FREE_SID);
-
- for (int j = 0; j < intsPerSector; j++) {
- int nextSID = input.readInt();
- int index = (j + (i * intsPerSector));
-
- shortSAT[index] = nextSID;
- }
- }
- }
-
- /**
- * Gets the SIdChain for the given stream Id
- *
- * @param pSId the stream Id
- * @param pStreamSize the size of the stream, or -1 for system control streams
- * @return the SIdChain for the given stream Id
- * @throws IOException if an I/O exception occurs
- */
- private SIdChain getSIdChain(final int pSId, final long pStreamSize) throws IOException {
- SIdChain chain = new SIdChain();
-
- int[] sat = isShortStream(pStreamSize) ? shortSAT : SAT;
-
- int sid = pSId;
- while (sid != END_OF_CHAIN_SID && sid != FREE_SID) {
- chain.addSID(sid);
- sid = sat[sid];
- }
-
- return chain;
- }
-
- private boolean isShortStream(final long pStreamSize) {
- return pStreamSize != FREE_SID && pStreamSize < minStreamSize;
- }
-
- /**
- * Seeks to the start pos for the given stream Id
- *
- * @param pSId the stream Id
- * @param pStreamSize the size of the stream, or -1 for system control streams
- * @throws IOException if an I/O exception occurs
- */
- private void seekToSId(final int pSId, final long pStreamSize) throws IOException {
- long pos;
-
- if (isShortStream(pStreamSize)) {
- // The short stream is not continuous...
- Entry root = getRootEntry();
- if (shortStreamSIdChain == null) {
- shortStreamSIdChain = getSIdChain(root.startSId, root.streamSize);
- }
-
-// System.err.println("pSId: " + pSId);
- int shortPerSId = sectorSize / shortSectorSize;
-// System.err.println("shortPerSId: " + shortPerSId);
- int offset = pSId / shortPerSId;
-// System.err.println("offset: " + offset);
- int shortOffset = pSId - (offset * shortPerSId);
-// System.err.println("shortOffset: " + shortOffset);
-// System.err.println("shortStreamSIdChain.offset: " + shortStreamSIdChain.get(offset));
-
- pos = HEADER_SIZE
- + (shortStreamSIdChain.get(offset) * (long) sectorSize)
- + (shortOffset * (long) shortSectorSize);
-// System.err.println("pos: " + pos);
- }
- else {
- pos = HEADER_SIZE + pSId * (long) sectorSize;
- }
-
- if (input instanceof LittleEndianRandomAccessFile) {
- ((LittleEndianRandomAccessFile) input).seek(pos);
- }
- else if (input instanceof ImageInputStream) {
- ((ImageInputStream) input).seek(pos);
- }
- else {
- ((SeekableLittleEndianDataInputStream) input).seek(pos);
- }
- }
-
- private void seekToDId(final int pDId) throws IOException {
- if (directorySIdChain == null) {
- directorySIdChain = getSIdChain(directorySId, FREE_SID);
- }
-
- int dIdsPerSId = sectorSize / Entry.LENGTH;
-
- int sIdOffset = pDId / dIdsPerSId;
- int dIdOffset = pDId - (sIdOffset * dIdsPerSId);
-
- int sId = directorySIdChain.get(sIdOffset);
-
- seekToSId(sId, FREE_SID);
- if (input instanceof LittleEndianRandomAccessFile) {
- LittleEndianRandomAccessFile input = (LittleEndianRandomAccessFile) this.input;
- input.seek(input.getFilePointer() + dIdOffset * Entry.LENGTH);
- }
- else if (input instanceof ImageInputStream) {
- ImageInputStream input = (ImageInputStream) this.input;
- input.seek(input.getStreamPosition() + dIdOffset * Entry.LENGTH);
- }
- else {
- SeekableLittleEndianDataInputStream input = (SeekableLittleEndianDataInputStream) this.input;
- input.seek(input.getStreamPosition() + dIdOffset * Entry.LENGTH);
- }
- }
-
- SeekableInputStream getInputStreamForSId(final int pStreamId, final int pStreamSize) throws IOException {
- SIdChain chain = getSIdChain(pStreamId, pStreamSize);
-
- // TODO: Detach? Means, we have to copy to a byte buffer, or keep track of
- // positions, and seek back and forth (would be cool, but difficult)..
- int sectorSize = pStreamSize < minStreamSize ? shortSectorSize : this.sectorSize;
-
- return new MemoryCacheSeekableStream(new Stream(chain, pStreamSize, sectorSize, this));
- }
-
- private InputStream getDirectoryStreamForDId(final int pDirectoryId) throws IOException {
- // This is always exactly 128 bytes, so we'll just read it all,
- // and buffer (we might want to optimize this later).
- byte[] bytes = new byte[Entry.LENGTH];
-
- seekToDId(pDirectoryId);
- input.readFully(bytes);
-
- return new ByteArrayInputStream(bytes);
- }
-
- Entry getEntry(final int pDirectoryId, Entry pParent) throws IOException {
- Entry entry = Entry.readEntry(new LittleEndianDataInputStream(
- getDirectoryStreamForDId(pDirectoryId)
- ));
- entry.parent = pParent;
- entry.document = this;
- return entry;
- }
-
- SortedSet getEntries(final int pDirectoryId, final Entry pParent)
- throws IOException {
- return getEntriesRecursive(pDirectoryId, pParent, new TreeSet());
- }
-
- private SortedSet getEntriesRecursive(final int pDirectoryId, final Entry pParent, final SortedSet pEntries)
- throws IOException {
-
- //System.out.println("pDirectoryId: " + pDirectoryId);
-
- Entry entry = getEntry(pDirectoryId, pParent);
-
- //System.out.println("entry: " + entry);
-
- if (!pEntries.add(entry)) {
- // TODO: This occurs in some Thumbs.db files, and Windows will
- // still parse the file gracefully somehow...
- // Deleting and regenerating the file will remove the cyclic
- // references, but... How can Windows parse this file?
- throw new CorruptDocumentException("Cyclic chain reference for entry: " + pDirectoryId);
- }
-
- if (entry.prevDId != FREE_SID) {
- //System.out.println("prevDId: " + entry.prevDId);
- getEntriesRecursive(entry.prevDId, pParent, pEntries);
- }
- if (entry.nextDId != FREE_SID) {
- //System.out.println("nextDId: " + entry.nextDId);
- getEntriesRecursive(entry.nextDId, pParent, pEntries);
- }
-
- return pEntries;
- }
-
- /*public*/ Entry getEntry(String pPath) throws IOException {
- if (StringUtil.isEmpty(pPath) || !pPath.startsWith("/")) {
- throw new IllegalArgumentException("Path must be absolute, and contain a valid path: " + pPath);
- }
-
- Entry entry = getRootEntry();
- if (pPath.equals("/")) {
- // '/' means root entry
- return entry;
- }
- else {
- // Otherwise get children recursively:
- String[] pathElements = StringUtil.toStringArray(pPath, "/");
- for (String pathElement : pathElements) {
- entry = entry.getChildEntry(pathElement);
-
- // No such child...
- if (entry == null) {
- break;// TODO: FileNotFoundException? Should behave like Entry.getChildEntry!!
- }
- }
- return entry;
- }
- }
-
- public Entry getRootEntry() throws IOException {
- if (rootEntry == null) {
- readSAT();
-
- rootEntry = getEntry(0, null);
-
- if (rootEntry.type != Entry.ROOT_STORAGE) {
- throw new CorruptDocumentException("Invalid root storage type: " + rootEntry.type);
- }
- }
-
- return rootEntry;
- }
-
- // This is useless, as most documents on file have all-zero UUIDs...
-// @Override
-// public int hashCode() {
-// return uUID.hashCode();
-// }
-//
-// @Override
-// public boolean equals(final Object pOther) {
-// if (pOther == this) {
-// return true;
-// }
-//
-// if (pOther == null) {
-// return true;
-// }
-//
-// if (pOther.getClass() == getClass()) {
-// return uUID.equals(((CompoundDocument) pOther).uUID);
-// }
-//
-// return false;
-// }
-
- @Override
- public String toString() {
- return String.format(
- "%s[uuid: %s, sector size: %d/%d bytes, directory SID: %d, master SAT: %s entries]",
- getClass().getSimpleName(), uUID, sectorSize, shortSectorSize, directorySId, masterSAT.length
- );
- }
-
- /**
- * Converts the given time stamp to standard Java time representation,
- * milliseconds since January 1, 1970.
- * The time stamp parameter is assumed to be in units of
- * 100 nano seconds since January 1, 1601.
- *
- * If the timestamp is {@code 0L} (meaning not specified), no conversion
- * is done, to behave like {@code java.io.File}.
- *
- * @param pMSTime an unsigned long value representing the time stamp (in
- * units of 100 nano seconds since January 1, 1601).
- *
- * @return the time stamp converted to Java time stamp in milliseconds,
- * or {@code 0L} if {@code pMSTime == 0L}
- */
- public static long toJavaTimeInMillis(final long pMSTime) {
- // NOTE: The time stamp field is an unsigned 64-bit integer value that
- // contains the time elapsed since 1601-Jan-01 00:00:00 (Gregorian
- // calendar).
- // One unit of this value is equal to 100 nanoseconds).
- // That means, each second the time stamp value will be increased by
- // 10 million units.
-
- if (pMSTime == 0L) {
- return 0L; // This is just less confusing...
- }
-
- // Convert to milliseconds (signed),
- // then convert to Java std epoch (1970-Jan-01 00:00:00)
- return ((pMSTime >> 1) / 5000) + EPOCH_OFFSET;
- }
-
- static class Stream extends InputStream {
- private final SIdChain chain;
- private final CompoundDocument document;
- private final long length;
-
- private long streamPos;
- private int nextSectorPos;
- private byte[] buffer;
- private int bufferPos;
-
- public Stream(SIdChain chain, int streamSize, int sectorSize, CompoundDocument document) {
- this.chain = chain;
- this.length = streamSize;
-
- this.buffer = new byte[sectorSize];
- this.bufferPos = buffer.length;
-
- this.document = document;
- }
-
- @Override
- public int available() throws IOException {
- return (int) Math.min(buffer.length - bufferPos, length - streamPos);
- }
-
- public int read() throws IOException {
- if (available() <= 0) {
- if (!fillBuffer()) {
- return -1;
- }
- }
-
- streamPos++;
-
- return buffer[bufferPos++] & 0xff;
- }
-
- private boolean fillBuffer() throws IOException {
- if (streamPos < length && nextSectorPos < chain.length()) {
- // TODO: Sync on document.input here, and we are completely detached... :-)
- // TODO: Update: We also need to sync other places... :-P
- synchronized (document) {
- document.seekToSId(chain.get(nextSectorPos), length);
- document.input.readFully(buffer);
- }
-
- nextSectorPos++;
- bufferPos = 0;
-
- return true;
- }
-
- return false;
- }
-
- @Override
- public int read(byte b[], int off, int len) throws IOException {
- if (available() <= 0) {
- if (!fillBuffer()) {
- return -1;
- }
- }
-
- int toRead = Math.min(len, available());
-
- System.arraycopy(buffer, bufferPos, b, off, toRead);
- bufferPos += toRead;
- streamPos += toRead;
-
- return toRead;
- }
-
- @Override
- public void close() throws IOException {
- buffer = null;
- }
- }
-
- static class SeekableLittleEndianDataInputStream extends LittleEndianDataInputStream implements Seekable {
- private final SeekableInputStream seekable;
-
- public SeekableLittleEndianDataInputStream(final SeekableInputStream pInput) {
- super(pInput);
- seekable = pInput;
- }
-
- public void seek(final long pPosition) throws IOException {
- seekable.seek(pPosition);
- }
-
- public boolean isCachedFile() {
- return seekable.isCachedFile();
- }
-
- public boolean isCachedMemory() {
- return seekable.isCachedMemory();
- }
-
- public boolean isCached() {
- return seekable.isCached();
- }
-
- public long getStreamPosition() throws IOException {
- return seekable.getStreamPosition();
- }
-
- public long getFlushedPosition() throws IOException {
- return seekable.getFlushedPosition();
- }
-
- public void flushBefore(final long pPosition) throws IOException {
- seekable.flushBefore(pPosition);
- }
-
- public void flush() throws IOException {
- seekable.flush();
- }
-
- @Override
- public void reset() throws IOException {
- seekable.reset();
- }
-
- public void mark() {
- seekable.mark();
- }
- }
-}
+/*
+ * 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.*;
+import com.twelvemonkeys.lang.StringUtil;
+
+import javax.imageio.stream.ImageInputStream;
+import java.io.*;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import static com.twelvemonkeys.lang.Validate.notNull;
+
+/**
+ * Represents a read-only OLE2 compound document.
+ *
+ *
+ * NOTE: This class is not synchronized. Accessing the document or its
+ * entries from different threads, will need synchronization on the document
+ * instance.
+ *
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/ole2/CompoundDocument.java#4 $
+ */
+public final class CompoundDocument implements AutoCloseable {
+ // TODO: Write support...
+ // TODO: Properties: http://support.microsoft.com/kb/186898
+
+ static final byte[] MAGIC = new byte[]{
+ (byte) 0xD0, (byte) 0xCF, (byte) 0x11, (byte) 0xE0,
+ (byte) 0xA1, (byte) 0xB1, (byte) 0x1A, (byte) 0xE1,
+ };
+
+ private static final int FREE_SID = -1;
+ private static final int END_OF_CHAIN_SID = -2;
+ private static final int SAT_SECTOR_SID = -3; // Sector used by SAT
+ private static final int MSAT_SECTOR_SID = -4; // Sector used my Master SAT
+
+ public static final int HEADER_SIZE = 512;
+
+ /** The epoch offset of CompoundDocument time stamps */
+ public final static long EPOCH_OFFSET = -11644477200000L;
+
+ private final DataInput input;
+
+ private UUID uUID;
+
+ private int sectorSize;
+ private int shortSectorSize;
+
+ private int directorySId;
+
+ private int minStreamSize;
+
+ private int shortSATSId;
+ private int shortSATSize;
+
+ // Master Sector Allocation Table
+ private int[] masterSAT;
+ private int[] SAT;
+ private int[] shortSAT;
+
+ private Entry rootEntry;
+ private SIdChain shortStreamSIdChain;
+ private SIdChain directorySIdChain;
+
+ /**
+ * Creates a (for now) read only {@code CompoundDocument}.
+ *
+ * Warning! You must invoke {@link #close()} on the compound document
+ * created from this constructor when done, to avoid leaking file
+ * descriptors.
+ *
+ *
+ * @param file the file to read from
+ *
+ * @throws IOException if an I/O exception occurs while reading the header
+ */
+ public CompoundDocument(final File file) throws IOException {
+ // TODO: We need to close this (or it's underlying RAF)! Otherwise we're leaking file descriptors!
+ input = new LittleEndianRandomAccessFile(FileUtil.resolve(file), "r");
+
+ // TODO: Might be better to read header on first read operation?!
+ // OTOH: It's also good to be fail-fast, so at least we should make
+ // sure we're reading a valid document
+ readHeader();
+ }
+
+ /**
+ * Creates a read only {@code CompoundDocument}.
+ *
+ * @param pInput the input to read from.
+ *
+ * @throws IOException if an I/O exception occurs while reading the header
+ */
+ public CompoundDocument(final InputStream pInput) throws IOException {
+ this(new MemoryCacheSeekableStream(pInput));
+ }
+
+ // For testing only, consider exposing later
+ CompoundDocument(final SeekableInputStream stream) throws IOException {
+ input = new SeekableLittleEndianDataInputStream(stream);
+
+ // TODO: Might be better to read header on first read operation?!
+ // OTOH: It's also good to be fail-fast, so at least we should make
+ // sure we're reading a valid document
+ readHeader();
+ }
+
+ /**
+ * Creates a read only {@code CompoundDocument}.
+ *
+ * @param input the input to read from
+ *
+ * @throws IOException if an I/O exception occurs while reading the header
+ */
+ public CompoundDocument(final ImageInputStream input) throws IOException {
+ this.input = notNull(input, "input");
+
+ // This implementation only supports little endian (Intel) CompoundDocuments
+ input.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+
+ // TODO: Might be better to read header on first read operation?!
+ // OTOH: It's also good to be fail-fast, so at least we should make
+ // sure we're reading a valid document
+ readHeader();
+ }
+
+ /**
+ * This method will close the underlying {@link RandomAccessFile} if any,
+ * but will leave any stream created outside the document open.
+ *
+ * @see #CompoundDocument(File)
+ * @see RandomAccessFile#close()
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ @Override
+ public void close() throws IOException {
+ if (input instanceof RandomAccessFile) {
+ ((RandomAccessFile) input).close();
+ }
+ else if (input instanceof LittleEndianRandomAccessFile) {
+ ((LittleEndianRandomAccessFile) input).close();
+ }
+
+ // Other streams are left open
+ }
+
+ public static boolean canRead(final DataInput pInput) {
+ return canRead(pInput, true);
+ }
+
+ // TODO: Refactor.. Figure out what we really need to expose to ImageIO for
+ // easy reading of the Thumbs.db file
+ // It's probably safer to create one version for InputStream and one for File
+ private static boolean canRead(final DataInput pInput, final boolean pReset) {
+ long pos = FREE_SID;
+ if (pReset) {
+ try {
+ if (pInput instanceof InputStream && ((InputStream) pInput).markSupported()) {
+ ((InputStream) pInput).mark(8);
+ }
+ else if (pInput instanceof ImageInputStream) {
+ ((ImageInputStream) pInput).mark();
+ }
+ else if (pInput instanceof RandomAccessFile) {
+ pos = ((RandomAccessFile) pInput).getFilePointer();
+ }
+ else if (pInput instanceof LittleEndianRandomAccessFile) {
+ pos = ((LittleEndianRandomAccessFile) pInput).getFilePointer();
+ }
+ else {
+ return false;
+ }
+ }
+ catch (IOException ignore) {
+ return false;
+ }
+ }
+
+ try {
+ byte[] magic = new byte[8];
+ pInput.readFully(magic);
+ return Arrays.equals(magic, MAGIC);
+ }
+ catch (IOException ignore) {
+ // Ignore
+ }
+ finally {
+ if (pReset) {
+ try {
+ if (pInput instanceof InputStream && ((InputStream) pInput).markSupported()) {
+ ((InputStream) pInput).reset();
+ }
+ else if (pInput instanceof ImageInputStream) {
+ ((ImageInputStream) pInput).reset();
+ }
+ else if (pInput instanceof RandomAccessFile) {
+ ((RandomAccessFile) pInput).seek(pos);
+ }
+ else if (pInput instanceof LittleEndianRandomAccessFile) {
+ ((LittleEndianRandomAccessFile) pInput).seek(pos);
+ }
+ }
+ catch (IOException e) {
+ // TODO: This isn't actually good enough...
+ // Means something fucked up, and will fail...
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private void readHeader() throws IOException {
+ if (masterSAT != null) {
+ return;
+ }
+
+ if (!canRead(input, false)) {
+ throw new CorruptDocumentException("Not an OLE 2 Compound Document");
+ }
+
+ // UID (seems to be all 0s)
+ uUID = new UUID(input.readLong(), input.readLong());
+// System.out.println("uUID: " + uUID);
+
+ // int version =
+ input.readUnsignedShort();
+// System.out.println("version: " + version);
+ // int revision =
+ input.readUnsignedShort();
+// System.out.println("revision: " + revision);
+
+ int byteOrder = input.readUnsignedShort();
+// System.out.printf("byteOrder: 0x%04x\n", byteOrder);
+ if (byteOrder == 0xffff) {
+ throw new CorruptDocumentException("Cannot read big endian OLE 2 Compound Documents");
+ }
+ else if (byteOrder != 0xfffe) {
+ // Reversed, as I'm already reading little-endian
+ throw new CorruptDocumentException(String.format("Unknown byte order marker: 0x%04x, expected 0xfffe or 0xffff", byteOrder));
+ }
+
+ sectorSize = 1 << input.readUnsignedShort();
+// System.out.println("sectorSize: " + sectorSize + " bytes");
+ shortSectorSize = 1 << input.readUnsignedShort();
+// System.out.println("shortSectorSize: " + shortSectorSize + " bytes");
+
+ // Reserved
+ if (skipBytesFully(10) != 10) {
+ throw new CorruptDocumentException();
+ }
+
+ int SATSize = input.readInt();
+// System.out.println("normalSATSize: " + SATSize);
+
+ directorySId = input.readInt();
+// System.out.println("directorySId: " + directorySId);
+
+ // Reserved
+ if (skipBytesFully(4) != 4) {
+ throw new CorruptDocumentException();
+ }
+
+ minStreamSize = input.readInt();
+// System.out.println("minStreamSize: " + minStreamSize + " bytes");
+
+ shortSATSId = input.readInt();
+// System.out.println("shortSATSId: " + shortSATSId);
+ shortSATSize = input.readInt();
+// System.out.println("shortSATSize: " + shortSATSize);
+ int masterSATSId = input.readInt();
+// System.out.println("masterSATSId: " + masterSATSId);
+ int masterSATSize = input.readInt();
+// System.out.println("masterSATSize: " + masterSATSize);
+
+ // Read masterSAT: 436 bytes, containing up to 109 SIDs
+ //System.out.println("MSAT:");
+ masterSAT = new int[SATSize];
+ final int headerSIds = Math.min(SATSize, 109);
+ for (int i = 0; i < headerSIds; i++) {
+ masterSAT[i] = input.readInt();
+ //System.out.println("\tSID(" + i + "): " + masterSAT[i]);
+ }
+
+ if (masterSATSId == END_OF_CHAIN_SID) {
+ // End of chain
+ int freeSIdLength = 436 - (SATSize * 4);
+ if (skipBytesFully(freeSIdLength) != freeSIdLength) {
+ throw new CorruptDocumentException();
+ }
+ }
+ else {
+ // Parse the SIDs in the extended MasterSAT sectors...
+ seekToSId(masterSATSId, FREE_SID);
+
+ int index = headerSIds;
+ for (int i = 0; i < masterSATSize; i++) {
+ for (int j = 0; j < 127; j++) {
+ int sid = input.readInt();
+ switch (sid) {
+ case FREE_SID:// Free
+ break;
+ default:
+ masterSAT[index++] = sid;
+ break;
+ }
+ }
+
+ int next = input.readInt();
+ if (next == END_OF_CHAIN_SID) {// End of chain
+ break;
+ }
+
+ seekToSId(next, FREE_SID);
+ }
+ }
+ }
+
+ private int skipBytesFully(final int n) throws IOException {
+ int toSkip = n;
+
+ while (toSkip > 0) {
+ int skipped = input.skipBytes(n);
+ if (skipped <= 0) {
+ break;
+ }
+
+ toSkip -= skipped;
+ }
+
+ return n - toSkip;
+ }
+
+ private void readSAT() throws IOException {
+ if (SAT != null) {
+ return;
+ }
+
+ final int intsPerSector = sectorSize / 4;
+
+ // Read the Sector Allocation Table
+ SAT = new int[masterSAT.length * intsPerSector];
+
+ for (int i = 0; i < masterSAT.length; i++) {
+ seekToSId(masterSAT[i], FREE_SID);
+
+ for (int j = 0; j < intsPerSector; j++) {
+ int nextSID = input.readInt();
+ int index = (j + (i * intsPerSector));
+
+ SAT[index] = nextSID;
+ }
+ }
+
+ // Read the short-stream Sector Allocation Table
+ SIdChain chain = getSIdChain(shortSATSId, FREE_SID);
+ shortSAT = new int[shortSATSize * intsPerSector];
+ for (int i = 0; i < shortSATSize; i++) {
+ seekToSId(chain.get(i), FREE_SID);
+
+ for (int j = 0; j < intsPerSector; j++) {
+ int nextSID = input.readInt();
+ int index = (j + (i * intsPerSector));
+
+ shortSAT[index] = nextSID;
+ }
+ }
+ }
+
+ /**
+ * Gets the SIdChain for the given stream Id
+ *
+ * @param pSId the stream Id
+ * @param pStreamSize the size of the stream, or -1 for system control streams
+ * @return the SIdChain for the given stream Id
+ * @throws IOException if an I/O exception occurs
+ */
+ private SIdChain getSIdChain(final int pSId, final long pStreamSize) throws IOException {
+ SIdChain chain = new SIdChain();
+
+ int[] sat = isShortStream(pStreamSize) ? shortSAT : SAT;
+
+ int sid = pSId;
+ while (sid != END_OF_CHAIN_SID && sid != FREE_SID) {
+ chain.addSID(sid);
+ sid = sat[sid];
+ }
+
+ return chain;
+ }
+
+ private boolean isShortStream(final long pStreamSize) {
+ return pStreamSize != FREE_SID && pStreamSize < minStreamSize;
+ }
+
+ /**
+ * Seeks to the start pos for the given stream Id
+ *
+ * @param pSId the stream Id
+ * @param pStreamSize the size of the stream, or -1 for system control streams
+ * @throws IOException if an I/O exception occurs
+ */
+ private void seekToSId(final int pSId, final long pStreamSize) throws IOException {
+ long pos;
+
+ if (isShortStream(pStreamSize)) {
+ // The short stream is not continuous...
+ Entry root = getRootEntry();
+ if (shortStreamSIdChain == null) {
+ shortStreamSIdChain = getSIdChain(root.startSId, root.streamSize);
+ }
+
+// System.err.println("pSId: " + pSId);
+ int shortPerSId = sectorSize / shortSectorSize;
+// System.err.println("shortPerSId: " + shortPerSId);
+ int offset = pSId / shortPerSId;
+// System.err.println("offset: " + offset);
+ int shortOffset = pSId - (offset * shortPerSId);
+// System.err.println("shortOffset: " + shortOffset);
+// System.err.println("shortStreamSIdChain.offset: " + shortStreamSIdChain.get(offset));
+
+ pos = HEADER_SIZE
+ + (shortStreamSIdChain.get(offset) * (long) sectorSize)
+ + (shortOffset * (long) shortSectorSize);
+// System.err.println("pos: " + pos);
+ }
+ else {
+ pos = HEADER_SIZE + pSId * (long) sectorSize;
+ }
+
+ if (input instanceof LittleEndianRandomAccessFile) {
+ ((LittleEndianRandomAccessFile) input).seek(pos);
+ }
+ else if (input instanceof ImageInputStream) {
+ ((ImageInputStream) input).seek(pos);
+ }
+ else {
+ ((SeekableLittleEndianDataInputStream) input).seek(pos);
+ }
+ }
+
+ private void seekToDId(final int pDId) throws IOException {
+ if (directorySIdChain == null) {
+ directorySIdChain = getSIdChain(directorySId, FREE_SID);
+ }
+
+ int dIdsPerSId = sectorSize / Entry.LENGTH;
+
+ int sIdOffset = pDId / dIdsPerSId;
+ int dIdOffset = pDId - (sIdOffset * dIdsPerSId);
+
+ int sId = directorySIdChain.get(sIdOffset);
+
+ seekToSId(sId, FREE_SID);
+ if (input instanceof LittleEndianRandomAccessFile) {
+ LittleEndianRandomAccessFile input = (LittleEndianRandomAccessFile) this.input;
+ input.seek(input.getFilePointer() + dIdOffset * Entry.LENGTH);
+ }
+ else if (input instanceof ImageInputStream) {
+ ImageInputStream input = (ImageInputStream) this.input;
+ input.seek(input.getStreamPosition() + dIdOffset * Entry.LENGTH);
+ }
+ else {
+ SeekableLittleEndianDataInputStream input = (SeekableLittleEndianDataInputStream) this.input;
+ input.seek(input.getStreamPosition() + dIdOffset * Entry.LENGTH);
+ }
+ }
+
+ SeekableInputStream getInputStreamForSId(final int pStreamId, final int pStreamSize) throws IOException {
+ SIdChain chain = getSIdChain(pStreamId, pStreamSize);
+
+ // TODO: Detach? Means, we have to copy to a byte buffer, or keep track of
+ // positions, and seek back and forth (would be cool, but difficult)..
+ int sectorSize = pStreamSize < minStreamSize ? shortSectorSize : this.sectorSize;
+
+ return new MemoryCacheSeekableStream(new Stream(chain, pStreamSize, sectorSize, this));
+ }
+
+ private InputStream getDirectoryStreamForDId(final int pDirectoryId) throws IOException {
+ // This is always exactly 128 bytes, so we'll just read it all,
+ // and buffer (we might want to optimize this later).
+ byte[] bytes = new byte[Entry.LENGTH];
+
+ seekToDId(pDirectoryId);
+ input.readFully(bytes);
+
+ return new ByteArrayInputStream(bytes);
+ }
+
+ Entry getEntry(final int pDirectoryId, Entry pParent) throws IOException {
+ Entry entry = Entry.readEntry(new LittleEndianDataInputStream(
+ getDirectoryStreamForDId(pDirectoryId)
+ ));
+ entry.parent = pParent;
+ entry.document = this;
+ return entry;
+ }
+
+ SortedSet getEntries(final int pDirectoryId, final Entry pParent)
+ throws IOException {
+ return getEntriesRecursive(pDirectoryId, pParent, new TreeSet());
+ }
+
+ private SortedSet getEntriesRecursive(final int pDirectoryId, final Entry pParent, final SortedSet pEntries)
+ throws IOException {
+
+ //System.out.println("pDirectoryId: " + pDirectoryId);
+
+ Entry entry = getEntry(pDirectoryId, pParent);
+
+ //System.out.println("entry: " + entry);
+
+ if (!pEntries.add(entry)) {
+ // TODO: This occurs in some Thumbs.db files, and Windows will
+ // still parse the file gracefully somehow...
+ // Deleting and regenerating the file will remove the cyclic
+ // references, but... How can Windows parse this file?
+ throw new CorruptDocumentException("Cyclic chain reference for entry: " + pDirectoryId);
+ }
+
+ if (entry.prevDId != FREE_SID) {
+ //System.out.println("prevDId: " + entry.prevDId);
+ getEntriesRecursive(entry.prevDId, pParent, pEntries);
+ }
+ if (entry.nextDId != FREE_SID) {
+ //System.out.println("nextDId: " + entry.nextDId);
+ getEntriesRecursive(entry.nextDId, pParent, pEntries);
+ }
+
+ return pEntries;
+ }
+
+ /*public*/ Entry getEntry(String pPath) throws IOException {
+ if (StringUtil.isEmpty(pPath) || !pPath.startsWith("/")) {
+ throw new IllegalArgumentException("Path must be absolute, and contain a valid path: " + pPath);
+ }
+
+ Entry entry = getRootEntry();
+ if (pPath.equals("/")) {
+ // '/' means root entry
+ return entry;
+ }
+ else {
+ // Otherwise get children recursively:
+ String[] pathElements = StringUtil.toStringArray(pPath, "/");
+ for (String pathElement : pathElements) {
+ entry = entry.getChildEntry(pathElement);
+
+ // No such child...
+ if (entry == null) {
+ break;// TODO: FileNotFoundException? Should behave like Entry.getChildEntry!!
+ }
+ }
+ return entry;
+ }
+ }
+
+ public Entry getRootEntry() throws IOException {
+ if (rootEntry == null) {
+ readSAT();
+
+ rootEntry = getEntry(0, null);
+
+ if (rootEntry.type != Entry.ROOT_STORAGE) {
+ throw new CorruptDocumentException("Invalid root storage type: " + rootEntry.type);
+ }
+ }
+
+ return rootEntry;
+ }
+
+ // This is useless, as most documents on file have all-zero UUIDs...
+// @Override
+// public int hashCode() {
+// return uUID.hashCode();
+// }
+//
+// @Override
+// public boolean equals(final Object pOther) {
+// if (pOther == this) {
+// return true;
+// }
+//
+// if (pOther == null) {
+// return true;
+// }
+//
+// if (pOther.getClass() == getClass()) {
+// return uUID.equals(((CompoundDocument) pOther).uUID);
+// }
+//
+// return false;
+// }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "%s[uuid: %s, sector size: %d/%d bytes, directory SID: %d, master SAT: %s entries]",
+ getClass().getSimpleName(), uUID, sectorSize, shortSectorSize, directorySId, masterSAT.length
+ );
+ }
+
+ /**
+ * Converts the given time stamp to standard Java time representation,
+ * milliseconds since January 1, 1970.
+ * The time stamp parameter is assumed to be in units of
+ * 100 nano seconds since January 1, 1601.
+ *
+ * If the timestamp is {@code 0L} (meaning not specified), no conversion
+ * is done, to behave like {@code java.io.File}.
+ *
+ *
+ * @param pMSTime an unsigned long value representing the time stamp (in
+ * units of 100 nano seconds since January 1, 1601).
+ *
+ * @return the time stamp converted to Java time stamp in milliseconds,
+ * or {@code 0L} if {@code pMSTime == 0L}
+ */
+ public static long toJavaTimeInMillis(final long pMSTime) {
+ // NOTE: The time stamp field is an unsigned 64-bit integer value that
+ // contains the time elapsed since 1601-Jan-01 00:00:00 (Gregorian
+ // calendar).
+ // One unit of this value is equal to 100 nanoseconds).
+ // That means, each second the time stamp value will be increased by
+ // 10 million units.
+
+ if (pMSTime == 0L) {
+ return 0L; // This is just less confusing...
+ }
+
+ // Convert to milliseconds (signed),
+ // then convert to Java std epoch (1970-Jan-01 00:00:00)
+ return ((pMSTime >> 1) / 5000) + EPOCH_OFFSET;
+ }
+
+ static class Stream extends InputStream {
+ private final SIdChain chain;
+ private final CompoundDocument document;
+ private final long length;
+
+ private long streamPos;
+ private int nextSectorPos;
+ private byte[] buffer;
+ private int bufferPos;
+
+ public Stream(SIdChain chain, int streamSize, int sectorSize, CompoundDocument document) {
+ this.chain = chain;
+ this.length = streamSize;
+
+ this.buffer = new byte[sectorSize];
+ this.bufferPos = buffer.length;
+
+ this.document = document;
+ }
+
+ @Override
+ public int available() throws IOException {
+ return (int) Math.min(buffer.length - bufferPos, length - streamPos);
+ }
+
+ public int read() throws IOException {
+ if (available() <= 0) {
+ if (!fillBuffer()) {
+ return -1;
+ }
+ }
+
+ streamPos++;
+
+ return buffer[bufferPos++] & 0xff;
+ }
+
+ private boolean fillBuffer() throws IOException {
+ if (streamPos < length && nextSectorPos < chain.length()) {
+ // TODO: Sync on document.input here, and we are completely detached... :-)
+ // TODO: Update: We also need to sync other places... :-P
+ synchronized (document) {
+ document.seekToSId(chain.get(nextSectorPos), length);
+ document.input.readFully(buffer);
+ }
+
+ nextSectorPos++;
+ bufferPos = 0;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int read(byte b[], int off, int len) throws IOException {
+ if (available() <= 0) {
+ if (!fillBuffer()) {
+ return -1;
+ }
+ }
+
+ int toRead = Math.min(len, available());
+
+ System.arraycopy(buffer, bufferPos, b, off, toRead);
+ bufferPos += toRead;
+ streamPos += toRead;
+
+ return toRead;
+ }
+
+ @Override
+ public void close() throws IOException {
+ buffer = null;
+ }
+ }
+
+ static class SeekableLittleEndianDataInputStream extends LittleEndianDataInputStream implements Seekable {
+ private final SeekableInputStream seekable;
+
+ public SeekableLittleEndianDataInputStream(final SeekableInputStream pInput) {
+ super(pInput);
+ seekable = pInput;
+ }
+
+ public void seek(final long pPosition) throws IOException {
+ seekable.seek(pPosition);
+ }
+
+ public boolean isCachedFile() {
+ return seekable.isCachedFile();
+ }
+
+ public boolean isCachedMemory() {
+ return seekable.isCachedMemory();
+ }
+
+ public boolean isCached() {
+ return seekable.isCached();
+ }
+
+ public long getStreamPosition() throws IOException {
+ return seekable.getStreamPosition();
+ }
+
+ public long getFlushedPosition() throws IOException {
+ return seekable.getFlushedPosition();
+ }
+
+ public void flushBefore(final long pPosition) throws IOException {
+ seekable.flushBefore(pPosition);
+ }
+
+ public void flush() throws IOException {
+ seekable.flush();
+ }
+
+ @Override
+ public void reset() throws IOException {
+ seekable.reset();
+ }
+
+ public void mark() {
+ seekable.mark();
+ }
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/CorruptDocumentException.java b/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/CorruptDocumentException.java
index e7340407..9a6f020c 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/CorruptDocumentException.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/CorruptDocumentException.java
@@ -4,26 +4,28 @@
*
* 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 "TwelveMonkeys" 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 OWNER 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.
+ * * 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;
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/Entry.java b/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/Entry.java
index d4ac6b60..1364c7cc 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/Entry.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/Entry.java
@@ -1,340 +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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @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 {
- 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 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 NO_CHILDREN = Collections.unmodifiableSortedSet(new TreeSet());
-
- 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).
- *
- * 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).
- *
- * 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 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 Harald Kuhr
+ * @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 {
+ 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 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 NO_CHILDREN = Collections.unmodifiableSortedSet(new TreeSet());
+
+ 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).
+ *
+ * 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).
+ *
+ * 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 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);
+ }
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/SIdChain.java b/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/SIdChain.java
index b1bb7b73..ac48c99b 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/SIdChain.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/SIdChain.java
@@ -4,26 +4,28 @@
*
* 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 "TwelveMonkeys" 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 OWNER 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.
+ * * 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;
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/package-info.java b/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/package-info.java
index 0220f950..35412b61 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/package-info.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/ole2/package-info.java
@@ -1,3 +1,33 @@
+/*
+ * 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.
+ */
+
/**
* Contains classes for reading the contents of the
* Microsoft OLE 2 compound document format.
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/package_info.java b/common/common-io/src/main/java/com/twelvemonkeys/io/package_info.java
index 87a4749e..07897ee8 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/package_info.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/package_info.java
@@ -1,3 +1,32 @@
+/*
+ * 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.
+ */
/**
* Provides for system input and output through data streams, serialization and the file system.
*/
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/net/HTTPUtil.java b/common/common-io/src/main/java/com/twelvemonkeys/net/HTTPUtil.java
index 399adf29..d50e203e 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/net/HTTPUtil.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/net/HTTPUtil.java
@@ -4,26 +4,28 @@
*
* 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 "TwelveMonkeys" 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 OWNER 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.
+ * * 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;
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/net/MIMEUtil.java b/common/common-io/src/main/java/com/twelvemonkeys/net/MIMEUtil.java
index a576a5e4..3b787b3b 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/net/MIMEUtil.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/net/MIMEUtil.java
@@ -1,312 +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 "TwelveMonkeys" 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 OWNER 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 Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/net/MIMEUtil.java#5 $
- *
- * @see MIME Media Types
- */
-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> sExtToMIME = new HashMap>();
- private static Map> sUnmodifiableExtToMIME = Collections.unmodifiableMap(sExtToMIME);
-
- private static Map> sMIMEToExt = new HashMap>();
- private static Map> 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 extensions =
- Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(extStr, ";, ")));
-
- String typeStr = StringUtil.toLowerCase((String) entry.getValue());
- List 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 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 getMIMETypes(final String pFileExt) {
- List 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> 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 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/*, */* 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 getExtensions(final String pMIME) {
- String mime = bareMIME(StringUtil.toLowerCase(pMIME));
- if (mime.endsWith("/*")) {
- return getExtensionForWildcard(mime);
- }
- List extensions = sMIMEToExt.get(mime);
- return maskNull(extensions);
- }
-
- // Gets all extensions for a wildcard MIME type
- private static List getExtensionForWildcard(final String pMIME) {
- final String family = pMIME.substring(0, pMIME.length() - 1);
- Set extensions = new LinkedHashSet();
- for (Map.Entry> mimeToExt : sMIMEToExt.entrySet()) {
- if ("*/".equals(family) || mimeToExt.getKey().startsWith(family)) {
- extensions.addAll(mimeToExt.getValue());
- }
- }
- return Collections.unmodifiableList(new ArrayList(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> getExtensionMappings() {
- return sUnmodifiableMIMEToExt;
- }
-
- /**
- * Tests wehter the type is a subtype of the type family.
- *
- * @param pTypeFamily the MIME type family ({@code image/*, */*}, 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 maskNull(List pTypes) {
- return (pTypes == null) ? Collections.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 Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/net/MIMEUtil.java#5 $
+ *
+ * @see MIME Media Types
+ */
+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> sExtToMIME = new HashMap>();
+ private static Map> sUnmodifiableExtToMIME = Collections.unmodifiableMap(sExtToMIME);
+
+ private static Map> sMIMEToExt = new HashMap>();
+ private static Map> 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 extensions =
+ Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(extStr, ";, ")));
+
+ String typeStr = StringUtil.toLowerCase((String) entry.getValue());
+ List 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 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 getMIMETypes(final String pFileExt) {
+ List 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> 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 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/*, */* 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 getExtensions(final String pMIME) {
+ String mime = bareMIME(StringUtil.toLowerCase(pMIME));
+ if (mime.endsWith("/*")) {
+ return getExtensionForWildcard(mime);
+ }
+ List extensions = sMIMEToExt.get(mime);
+ return maskNull(extensions);
+ }
+
+ // Gets all extensions for a wildcard MIME type
+ private static List getExtensionForWildcard(final String pMIME) {
+ final String family = pMIME.substring(0, pMIME.length() - 1);
+ Set extensions = new LinkedHashSet();
+ for (Map.Entry> mimeToExt : sMIMEToExt.entrySet()) {
+ if ("*/".equals(family) || mimeToExt.getKey().startsWith(family)) {
+ extensions.addAll(mimeToExt.getValue());
+ }
+ }
+ return Collections.unmodifiableList(new ArrayList(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> getExtensionMappings() {
+ return sUnmodifiableMIMEToExt;
+ }
+
+ /**
+ * Tests wehter the type is a subtype of the type family.
+ *
+ * @param pTypeFamily the MIME type family ({@code image/*, */*}, 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 maskNull(List pTypes) {
+ return (pTypes == null) ? Collections.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();
+ }
}
\ No newline at end of file
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/net/package_info.java b/common/common-io/src/main/java/com/twelvemonkeys/net/package_info.java
index 0a0322ca..8fae1caa 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/net/package_info.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/net/package_info.java
@@ -1,3 +1,33 @@
+/*
+ * 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.
+ */
+
/**
* Provides classes for net access.
*/
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/xml/DOMSerializer.java b/common/common-io/src/main/java/com/twelvemonkeys/xml/DOMSerializer.java
index 4dd47830..e2975901 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/xml/DOMSerializer.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/xml/DOMSerializer.java
@@ -4,34 +4,35 @@
*
* 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 "TwelveMonkeys" 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 OWNER 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.
- */
+ * * 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.xml;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.DOMImplementationList;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
@@ -101,8 +102,9 @@ public final class DOMSerializer {
/**
* Specifies wether the serializer should use indentation and optimize for
* readability.
- *
- * Note: This is a hint, and may be ignored by DOM implemenations.
+ *
+ * Note: This is a hint, and may be ignored by DOM implementations.
+ *
*
* @param pPrettyPrint {@code true} to enable pretty printing
*/
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/xml/XMLSerializer.java b/common/common-io/src/main/java/com/twelvemonkeys/xml/XMLSerializer.java
old mode 100755
new mode 100644
index c1945f11..50ef4673
--- a/common/common-io/src/main/java/com/twelvemonkeys/xml/XMLSerializer.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/xml/XMLSerializer.java
@@ -4,26 +4,28 @@
*
* 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 "TwelveMonkeys" 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 OWNER 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.
+ * * 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.xml;
@@ -387,9 +389,10 @@ public class XMLSerializer {
private void writeDocument(final PrintWriter pOut, final Node pNode, final SerializationContext pContext) {
// Document fragments might not have child nodes...
if (pNode.hasChildNodes()) {
- NodeList nodes = pNode.getChildNodes();
- for (int i = 0; i < nodes.getLength(); i++) {
- writeNodeRecursive(pOut, nodes.item(i), pContext);
+ Node child = pNode.getFirstChild();
+ while (child != null) {
+ writeNodeRecursive(pOut, child, pContext);
+ child = child.getNextSibling();
}
}
}
@@ -446,9 +449,10 @@ public class XMLSerializer {
pOut.println();
}
- NodeList children = pNode.getChildNodes();
- for (int i = 0; i < children.getLength(); i++) {
- writeNodeRecursive(pOut, children.item(i), pContext.push());
+ Node child = pNode.getFirstChild();
+ while (child != null) {
+ writeNodeRecursive(pOut, child, pContext.push());
+ child = child.getNextSibling();
}
if (!pContext.preserveSpace) {
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/xml/package_info.java b/common/common-io/src/main/java/com/twelvemonkeys/xml/package_info.java
index 5aa03c91..ee8b3793 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/xml/package_info.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/xml/package_info.java
@@ -1,4 +1,33 @@
+/*
+ * 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.
+ */
/**
* Provides XML support classes.
*/
- package com.twelvemonkeys.xml;
\ No newline at end of file
+package com.twelvemonkeys.xml;
\ No newline at end of file
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/CompoundReaderTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/CompoundReaderTest.java
similarity index 54%
rename from common/common-io/src/test/java/com/twelvemonkeys/io/CompoundReaderTestCase.java
rename to common/common-io/src/test/java/com/twelvemonkeys/io/CompoundReaderTest.java
index a3a4ed2f..6e1fb796 100755
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/CompoundReaderTestCase.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/CompoundReaderTest.java
@@ -1,68 +1,98 @@
-package com.twelvemonkeys.io;
-
-import com.twelvemonkeys.lang.StringUtil;
-import com.twelvemonkeys.util.CollectionUtil;
-import org.junit.Test;
-
-import java.io.Reader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.List;
-import java.util.ArrayList;
-
-import static org.junit.Assert.*;
-
-/**
- * CompoundReaderTestCase
- *
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/CompoundReaderTestCase.java#2 $
- */
-public class CompoundReaderTestCase extends ReaderAbstractTestCase {
- protected Reader makeReader(String pInput) {
- // Split
- String[] input = StringUtil.toStringArray(pInput, " ");
- List readers = new ArrayList(input.length);
-
- // Reappend spaces...
- // TODO: Add other readers
- for (int i = 0; i < input.length; i++) {
- if (i != 0) {
- input[i] = " " + input[i];
- }
- readers.add(new StringReader(input[i]));
- }
-
- return new CompoundReader(readers.iterator());
- }
-
- @Test
- public void testNullConstructor() {
- try {
- new CompoundReader(null);
- fail("Should not allow null argument");
- }
- catch (RuntimeException e) {
- assertNotNull(e.getMessage());
- }
- }
-
- @Test
- public void testEmptyIteratorConstructor() throws IOException {
- Reader reader = new CompoundReader(CollectionUtil.iterator(new Reader[0]));
- assertEquals(-1, reader.read());
- }
-
- @Test
- public void testIteratorWithNullConstructor() throws IOException {
- try {
- new CompoundReader(CollectionUtil.iterator(new Reader[] {null}));
- fail("Should not allow null in iterator argument");
- }
- catch (RuntimeException e) {
- assertNotNull(e.getMessage());
- }
- }
-}
+/*
+ * 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.StringUtil;
+import com.twelvemonkeys.util.CollectionUtil;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+/**
+ * CompoundReaderTestCase
+ *
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/CompoundReaderTestCase.java#2 $
+ */
+public class CompoundReaderTest extends ReaderAbstractTest {
+ protected Reader makeReader(String pInput) {
+ // Split
+ String[] input = StringUtil.toStringArray(pInput, " ");
+ List readers = new ArrayList(input.length);
+
+ // Reappend spaces...
+ // TODO: Add other readers
+ for (int i = 0; i < input.length; i++) {
+ if (i != 0) {
+ input[i] = " " + input[i];
+ }
+ readers.add(new StringReader(input[i]));
+ }
+
+ return new CompoundReader(readers.iterator());
+ }
+
+ @Test
+ public void testNullConstructor() {
+ try {
+ new CompoundReader(null);
+ fail("Should not allow null argument");
+ }
+ catch (RuntimeException e) {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testEmptyIteratorConstructor() throws IOException {
+ Reader reader = new CompoundReader(CollectionUtil.iterator(new Reader[0]));
+ assertEquals(-1, reader.read());
+ }
+
+ @Test
+ public void testIteratorWithNullConstructor() throws IOException {
+ try {
+ new CompoundReader(CollectionUtil.iterator(new Reader[] {null}));
+ fail("Should not allow null in iterator argument");
+ }
+ catch (RuntimeException e) {
+ assertNotNull(e.getMessage());
+ }
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/FastByteArrayOutputStreamTest.java b/common/common-io/src/test/java/com/twelvemonkeys/io/FastByteArrayOutputStreamTest.java
new file mode 100755
index 00000000..c3537431
--- /dev/null
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/FastByteArrayOutputStreamTest.java
@@ -0,0 +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;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * FastByteArrayOutputStreamTestCase
+ *
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/FastByteArrayOutputStreamTestCase.java#1 $
+ */
+public class FastByteArrayOutputStreamTest extends OutputStreamAbstractTest {
+ protected FastByteArrayOutputStream makeObject() {
+ return new FastByteArrayOutputStream(256);
+ }
+
+ @Test
+ public void testCreateInputStream() throws IOException {
+ FastByteArrayOutputStream out = makeObject();
+
+ String hello = "Hello World";
+ out.write(hello.getBytes("UTF-8"));
+
+ InputStream in = out.createInputStream();
+
+ byte[] read = FileUtil.read(in);
+
+ assertEquals(hello, new String(read, "UTF-8"));
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/FastByteArrayOutputStreamTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/FastByteArrayOutputStreamTestCase.java
deleted file mode 100755
index 95f09ccf..00000000
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/FastByteArrayOutputStreamTestCase.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.twelvemonkeys.io;
-
-import org.junit.Test;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import static org.junit.Assert.assertEquals;
-
-/**
- * FastByteArrayOutputStreamTestCase
- *
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/FastByteArrayOutputStreamTestCase.java#1 $
- */
-public class FastByteArrayOutputStreamTestCase extends OutputStreamAbstractTestCase {
- protected FastByteArrayOutputStream makeObject() {
- return new FastByteArrayOutputStream(256);
- }
-
- @Test
- public void testCreateInputStream() throws IOException {
- FastByteArrayOutputStream out = makeObject();
-
- String hello = "Hello World";
- out.write(hello.getBytes("UTF-8"));
-
- InputStream in = out.createInputStream();
-
- byte[] read = FileUtil.read(in);
-
- assertEquals(hello, new String(read, "UTF-8"));
- }
-}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/FileCacheSeekableStreamTest.java b/common/common-io/src/test/java/com/twelvemonkeys/io/FileCacheSeekableStreamTest.java
new file mode 100755
index 00000000..01d139b1
--- /dev/null
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/FileCacheSeekableStreamTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+/**
+ * FileCacheSeekableStreamTestCase
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/FileCacheSeekableStreamTestCase.java#3 $
+ */
+public class FileCacheSeekableStreamTest extends SeekableInputStreamAbstractTest {
+ protected SeekableInputStream makeInputStream(final InputStream pStream) {
+ try {
+ return new FileCacheSeekableStream(pStream);
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/FileCacheSeekableStreamTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/FileCacheSeekableStreamTestCase.java
deleted file mode 100755
index d12e9ca5..00000000
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/FileCacheSeekableStreamTestCase.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.twelvemonkeys.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * FileCacheSeekableStreamTestCase
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/FileCacheSeekableStreamTestCase.java#3 $
- */
-public class FileCacheSeekableStreamTestCase extends SeekableInputStreamAbstractTestCase {
- protected SeekableInputStream makeInputStream(final InputStream pStream) {
- try {
- return new FileCacheSeekableStream(pStream);
- }
- catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/FileSeekableStreamTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/FileSeekableStreamTest.java
similarity index 55%
rename from common/common-io/src/test/java/com/twelvemonkeys/io/FileSeekableStreamTestCase.java
rename to common/common-io/src/test/java/com/twelvemonkeys/io/FileSeekableStreamTest.java
index 65f3c989..d4325af9 100755
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/FileSeekableStreamTestCase.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/FileSeekableStreamTest.java
@@ -1,74 +1,104 @@
-package com.twelvemonkeys.io;
-
-import org.junit.Test;
-
-import java.io.*;
-
-import static org.junit.Assert.*;
-
-/**
- * MemoryCacheSeekableStreamTestCase
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/FileSeekableStreamTestCase.java#3 $
- */
-public class FileSeekableStreamTestCase extends SeekableInputStreamAbstractTestCase {
- protected SeekableInputStream makeInputStream(final InputStream pStream) {
- try {
- return new FileSeekableStream(createFileWithContent(pStream));
- }
- catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- private File createFileWithContent(final InputStream pStream) throws IOException {
- File temp = File.createTempFile("tm-io-junit", null);
- temp.deleteOnExit();
- OutputStream os = new FileOutputStream(temp);
- try {
- FileUtil.copy(pStream, os);
- }
- finally {
- os.close();
- pStream.close();
- }
- return temp;
- }
-
- @Test
- @Override
- public void testCloseUnderlyingStream() throws IOException {
- // There is no underlying stream here...
- }
-
- @Test
- public void testCloseUnderlyingFile() throws IOException {
- final boolean[] closed = new boolean[1];
-
- File file = createFileWithContent(new ByteArrayInputStream(makeRandomArray(256)));
-
- RandomAccessFile raf = new RandomAccessFile(file, "r") {
- @Override
- public void close() throws IOException {
- closed[0] = true;
- super.close();
- }
- };
-
- FileSeekableStream stream = new FileSeekableStream(raf);
-
- try {
- FileUtil.read(stream); // Read until EOF
-
- assertEquals("EOF not reached (test case broken)", -1, stream.read());
- assertFalse("Underlying stream closed before close", closed[0]);
- }
- finally {
- stream.close();
- }
-
- assertTrue("Underlying stream not closed", closed[0]);
- }
-}
+/*
+ * 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 org.junit.Test;
+
+import java.io.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * MemoryCacheSeekableStreamTestCase
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/FileSeekableStreamTestCase.java#3 $
+ */
+public class FileSeekableStreamTest extends SeekableInputStreamAbstractTest {
+ protected SeekableInputStream makeInputStream(final InputStream pStream) {
+ try {
+ return new FileSeekableStream(createFileWithContent(pStream));
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private File createFileWithContent(final InputStream pStream) throws IOException {
+ File temp = File.createTempFile("tm-io-junit", null);
+ temp.deleteOnExit();
+ OutputStream os = new FileOutputStream(temp);
+ try {
+ FileUtil.copy(pStream, os);
+ }
+ finally {
+ os.close();
+ pStream.close();
+ }
+ return temp;
+ }
+
+ @Test
+ @Override
+ public void testCloseUnderlyingStream() throws IOException {
+ // There is no underlying stream here...
+ }
+
+ @Test
+ public void testCloseUnderlyingFile() throws IOException {
+ final boolean[] closed = new boolean[1];
+
+ File file = createFileWithContent(new ByteArrayInputStream(makeRandomArray(256)));
+
+ RandomAccessFile raf = new RandomAccessFile(file, "r") {
+ @Override
+ public void close() throws IOException {
+ closed[0] = true;
+ super.close();
+ }
+ };
+
+ FileSeekableStream stream = new FileSeekableStream(raf);
+
+ try {
+ FileUtil.read(stream); // Read until EOF
+
+ assertEquals("EOF not reached (test case broken)", -1, stream.read());
+ assertFalse("Underlying stream closed before close", closed[0]);
+ }
+ finally {
+ stream.close();
+ }
+
+ assertTrue("Underlying stream not closed", closed[0]);
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/InputStreamAbstractTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/InputStreamAbstractTest.java
similarity index 89%
rename from common/common-io/src/test/java/com/twelvemonkeys/io/InputStreamAbstractTestCase.java
rename to common/common-io/src/test/java/com/twelvemonkeys/io/InputStreamAbstractTest.java
index 9f21a06d..59e1ac38 100755
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/InputStreamAbstractTestCase.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/InputStreamAbstractTest.java
@@ -1,419 +1,448 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.twelvemonkeys.io;
-
-import com.twelvemonkeys.lang.ObjectAbstractTestCase;
-import org.junit.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Random;
-
-import static org.junit.Assert.*;
-
-/**
- * InputStreamAbstractTestCase
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/InputStreamAbstractTestCase.java#1 $
- */
-public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase {
- // TODO: FixMe! THIS TEST IS (WAS) COMPLETELY BROKEN...
- // It relies on the contents of the stream being a certain order byte0 == 0, byte1 == 1 etc..
- // But the subclasses don't implement this.. Need to fix.
-
- final static private long SEED = 29487982745l;
- final static Random sRandom = new Random(SEED);
-
- protected final Object makeObject() {
- return makeInputStream();
- }
-
- protected InputStream makeInputStream() {
- return makeInputStream(16);
- }
-
- protected InputStream makeInputStream(int pSize) {
- byte[] bytes = makeRandomArray(pSize);
- return makeInputStream(bytes);
- }
-
- protected abstract InputStream makeInputStream(byte[] pBytes);
-
- protected final byte[] makeRandomArray(final int pSize) {
- byte[] bytes = new byte[pSize];
- sRandom.nextBytes(bytes);
- return bytes;
- }
-
- protected final byte[] makeOrderedArray(final int pSize) {
- byte[] bytes = new byte[pSize];
- for (int i = 0; i < bytes.length; i++) {
- bytes[i] = (byte) i;
- }
- return bytes;
- }
-
- @Test
- public void testRead() throws Exception {
- int size = 5;
- InputStream input = makeInputStream(makeOrderedArray(size));
- for (int i = 0; i < size; i++) {
- assertTrue("Check Size [" + i + "]", (size - i) >= input.available());
- assertEquals("Check Value [" + i + "]", i, input.read());
- }
- assertEquals("Available after contents all read", 0, input.available());
-
- // Test reading after the end of file
- try {
- int result = input.read();
- assertEquals("Wrong value read after end of file", -1, result);
- }
- catch (IOException e) {
- fail("Should not have thrown an IOException: " + e.getMessage());
- }
- }
-
- @Test
- public void testAvailable() throws Exception {
- InputStream input = makeInputStream(1);
- assertFalse("Unexpected EOF", input.read() < 0);
- assertEquals("Available after contents all read", 0, input.available());
-
- // Check availbale is zero after End of file
- assertEquals("End of File", -1, input.read());
- assertEquals("Available after End of File", 0, input.available());
- }
-
- @Test
- public void testReadByteArray() throws Exception {
- byte[] bytes = new byte[10];
- byte[] data = makeOrderedArray(15);
- InputStream input = makeInputStream(data);
-
- // Read into array
- int count1 = input.read(bytes);
- assertEquals("Read 1", bytes.length, count1);
- for (int i = 0; i < count1; i++) {
- assertEquals("Check Bytes 1", i, bytes[i]);
- }
-
- // Read into array
- int count2 = input.read(bytes);
- assertEquals("Read 2", 5, count2);
- for (int i = 0; i < count2; i++) {
- assertEquals("Check Bytes 2", count1 + i, bytes[i]);
- }
-
- // End of File
- int count3 = input.read(bytes);
- assertEquals("Read 3 (EOF)", -1, count3);
-
- // Test reading after the end of file
- try {
- int result = input.read(bytes);
- assertEquals("Wrong value read after end of file", -1, result);
- }
- catch (IOException e) {
- fail("Should not have thrown an IOException: " + e.getMessage());
- }
-
- // Reset
- input = makeInputStream(data);
-
- // Read into array using offset & length
- int offset = 2;
- int lth = 4;
- int count5 = input.read(bytes, offset, lth);
- assertEquals("Read 5", lth, count5);
- for (int i = offset; i < lth; i++) {
- assertEquals("Check Bytes 2", i - offset, bytes[i]);
- }
- }
-
- @Test
- public void testEOF() throws Exception {
- InputStream input = makeInputStream(makeOrderedArray(2));
- assertEquals("Read 1", 0, input.read());
- assertEquals("Read 2", 1, input.read());
- assertEquals("Read 3", -1, input.read());
- assertEquals("Read 4", -1, input.read());
- assertEquals("Read 5", -1, input.read());
- }
-
- @Test
- public void testMarkResetUnsupported() throws IOException {
- InputStream input = makeInputStream(10);
- if (input.markSupported()) {
- return;
- }
-
- input.mark(100); // Should be a no-op
-
- int read = input.read();
- assertTrue(read >= 0);
-
- // TODO: According to InputStream#reset, it is allowed to do some
- // implementation specific reset, and still be correct...
- try {
- input.reset();
- fail("Should throw IOException");
- }
- catch (IOException e) {
- assertTrue("Wrong messge: " + e.getMessage(), e.getMessage().contains("reset"));
- }
- }
-
- @Test
- public void testResetNoMark() throws Exception {
- InputStream input = makeInputStream(makeOrderedArray(10));
-
- if (!input.markSupported()) {
- return; // Not supported, skip test
- }
-
- int read = input.read();
- assertEquals(0, read);
-
- // No mark may either throw exception, or reset to beginning of stream.
- try {
- input.reset();
- assertEquals("Re-read of reset data should be same", 0, input.read());
- }
- catch (Exception e) {
- assertTrue("Wrong no mark IOException message", e.getMessage().contains("mark"));
- }
- }
-
- @Test
- public void testMarkReset() throws Exception {
- InputStream input = makeInputStream(makeOrderedArray(25));
-
- if (!input.markSupported()) {
- return; // Not supported, skip test
- }
-
- int read = input.read();
- assertEquals(0, read);
-
- int position = 1;
- int readlimit = 10;
-
- // Mark
- input.mark(readlimit);
-
- // Read further
- for (int i = 0; i < 3; i++) {
- assertEquals("Read After Mark [" + i + "]", (position + i), input.read());
- }
-
- // Reset
- input.reset();
-
- // Read from marked position
- for (int i = 0; i < readlimit + 1; i++) {
- assertEquals("Read After Reset [" + i + "]", (position + i), input.read());
- }
- }
-
- @Test
- public void testResetAfterReadLimit() throws Exception {
- InputStream input = makeInputStream(makeOrderedArray(25));
-
- if (!input.markSupported()) {
- return; // Not supported, skip test
- }
-
- int read = input.read();
- assertEquals(0, read);
-
- int position = 1;
- int readlimit = 5;
-
- // Mark
- input.mark(readlimit);
-
- // Read past marked position
- for (int i = 0; i < readlimit + 1; i++) {
- assertEquals("Read After Reset [" + i + "]", (position + i), input.read());
- }
-
- // Reset after read limit passed, may either throw exception, or reset to last mark
- try {
- input.reset();
- assertEquals("Re-read of reset data should be same", 1, input.read());
- }
- catch (Exception e) {
- assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
- }
- }
-
- @Test
- public void testResetAfterReset() throws Exception {
- InputStream input = makeInputStream(makeOrderedArray(25));
-
- if (!input.markSupported()) {
- return; // Not supported, skip test
- }
-
- int first = input.read();
- assertTrue("Expected to read positive value", first >= 0);
-
- int readlimit = 5;
-
- // Mark
- input.mark(readlimit);
- int read = input.read();
- assertTrue("Expected to read positive value", read >= 0);
-
- assertTrue(input.read() >= 0);
- assertTrue(input.read() >= 0);
-
- input.reset();
- assertEquals("Expected value read differs from actual", read, input.read());
-
- // Reset after read limit passed, may either throw exception, or reset to last good mark
- try {
- input.reset();
- int reRead = input.read();
- assertTrue("Re-read of reset data should be same as initially marked or first", reRead == read || reRead == first);
- }
- catch (Exception e) {
- assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
- }
- }
-
- @Test
- public void testSkip() throws Exception {
- InputStream input = makeInputStream(makeOrderedArray(10));
-
- assertEquals("Unexpected value read", 0, input.read());
- assertEquals("Unexpected value read", 1, input.read());
- assertEquals("Unexpected number of bytes skipped", 5, input.skip(5));
- assertEquals("Unexpected value read", 7, input.read());
-
- assertEquals("Unexpected number of bytes skipped", 2, input.skip(5)); // only 2 left to skip
- assertEquals("Unexpected value read after EOF", -1, input.read());
-
- // Spec says skip might return 0 or negative after EOF...
- assertTrue("Positive value skipped after EOF", input.skip(5) <= 0); // End of file
- assertEquals("Unexpected value read after EOF", -1, input.read());
- }
-
- @Test
- public void testSanityOrdered() throws IOException {
- // This is to sanity check that the test itself is correct...
- byte[] bytes = makeOrderedArray(25);
- InputStream expected = new ByteArrayInputStream(bytes);
- InputStream actual = makeInputStream(bytes);
-
- for (byte b : bytes) {
- assertEquals((int) b, expected.read());
- assertEquals((int) b, actual.read());
- }
- }
-
- @Test
- public void testSanityOrdered2() throws IOException {
- // This is to sanity check that the test itself is correct...
- byte[] bytes = makeOrderedArray(25);
- InputStream expected = new ByteArrayInputStream(bytes);
- InputStream actual = makeInputStream(bytes);
-
- byte[] e = new byte[bytes.length];
- byte[] a = new byte[bytes.length];
-
- assertEquals(e.length, expected.read(e, 0, e.length));
- assertEquals(a.length, actual.read(a, 0, a.length));
-
- for (int i = 0; i < bytes.length; i++) {
- assertEquals(bytes[i], e[i]);
- assertEquals(bytes[i], a[i]);
- }
- }
-
- @Test
- public void testSanityNegative() throws IOException {
- // This is to sanity check that the test itself is correct...
- byte[] bytes = new byte[25];
- for (int i = 0; i < bytes.length; i++) {
- bytes[i] = (byte) (255 - i);
- }
- InputStream expected = new ByteArrayInputStream(bytes);
- InputStream actual = makeInputStream(bytes);
-
- for (byte b : bytes) {
- assertEquals(b & 0xff, expected.read());
- assertEquals(b & 0xff, actual.read());
- }
- }
-
- @Test
- public void testSanityNegative2() throws IOException {
- // This is to sanity check that the test itself is correct...
- byte[] bytes = new byte[25];
- for (int i = 0; i < bytes.length; i++) {
- bytes[i] = (byte) (255 - i);
- }
- InputStream expected = new ByteArrayInputStream(bytes);
- InputStream actual = makeInputStream(bytes);
-
- byte[] e = new byte[bytes.length];
- byte[] a = new byte[bytes.length];
-
- assertEquals(e.length, expected.read(e, 0, e.length));
- assertEquals(a.length, actual.read(a, 0, a.length));
-
- for (int i = 0; i < bytes.length; i++) {
- assertEquals(bytes[i], e[i]);
- assertEquals(bytes[i], a[i]);
- }
- }
-
- @Test
- public void testSanityRandom() throws IOException {
- // This is to sanity check that the test itself is correct...
- byte[] bytes = makeRandomArray(25);
- InputStream expected = new ByteArrayInputStream(bytes);
- InputStream actual = makeInputStream(bytes);
-
- for (byte b : bytes) {
- assertEquals(b & 0xff, expected.read());
- assertEquals(b & 0xff, actual.read());
- }
- }
-
- @Test
- public void testSanityRandom2() throws IOException {
- // This is to sanity check that the test itself is correct...
- byte[] bytes = makeRandomArray(25);
- InputStream expected = new ByteArrayInputStream(bytes);
- InputStream actual = makeInputStream(bytes);
-
- byte[] e = new byte[bytes.length];
- byte[] a = new byte[bytes.length];
-
- assertEquals(e.length, expected.read(e, 0, e.length));
- assertEquals(a.length, actual.read(a, 0, a.length));
-
- for (int i = 0; i < bytes.length; i++) {
- assertEquals(bytes[i], e[i]);
- assertEquals(bytes[i], a[i]);
- }
- }}
+/*
+ * 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.
+ */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.twelvemonkeys.io;
+
+import com.twelvemonkeys.lang.ObjectAbstractTest;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+/**
+ * InputStreamAbstractTestCase
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/InputStreamAbstractTestCase.java#1 $
+ */
+public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
+ // TODO: FixMe! THIS TEST IS (WAS) COMPLETELY BROKEN...
+ // It relies on the contents of the stream being a certain order byte0 == 0, byte1 == 1 etc..
+ // But the subclasses don't implement this.. Need to fix.
+
+ final static private long SEED = 29487982745l;
+ final static Random sRandom = new Random(SEED);
+
+ protected final Object makeObject() {
+ return makeInputStream();
+ }
+
+ protected InputStream makeInputStream() {
+ return makeInputStream(16);
+ }
+
+ protected InputStream makeInputStream(int pSize) {
+ byte[] bytes = makeRandomArray(pSize);
+ return makeInputStream(bytes);
+ }
+
+ protected abstract InputStream makeInputStream(byte[] pBytes);
+
+ protected final byte[] makeRandomArray(final int pSize) {
+ byte[] bytes = new byte[pSize];
+ sRandom.nextBytes(bytes);
+ return bytes;
+ }
+
+ protected final byte[] makeOrderedArray(final int pSize) {
+ byte[] bytes = new byte[pSize];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = (byte) i;
+ }
+ return bytes;
+ }
+
+ @Test
+ public void testRead() throws Exception {
+ int size = 5;
+ InputStream input = makeInputStream(makeOrderedArray(size));
+ for (int i = 0; i < size; i++) {
+ assertTrue("Check Size [" + i + "]", (size - i) >= input.available());
+ assertEquals("Check Value [" + i + "]", i, input.read());
+ }
+ assertEquals("Available after contents all read", 0, input.available());
+
+ // Test reading after the end of file
+ try {
+ int result = input.read();
+ assertEquals("Wrong value read after end of file", -1, result);
+ }
+ catch (IOException e) {
+ fail("Should not have thrown an IOException: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testAvailable() throws Exception {
+ InputStream input = makeInputStream(1);
+ assertFalse("Unexpected EOF", input.read() < 0);
+ assertEquals("Available after contents all read", 0, input.available());
+
+ // Check availbale is zero after End of file
+ assertEquals("End of File", -1, input.read());
+ assertEquals("Available after End of File", 0, input.available());
+ }
+
+ @Test
+ public void testReadByteArray() throws Exception {
+ byte[] bytes = new byte[10];
+ byte[] data = makeOrderedArray(15);
+ InputStream input = makeInputStream(data);
+
+ // Read into array
+ int count1 = input.read(bytes);
+ assertEquals("Read 1", bytes.length, count1);
+ for (int i = 0; i < count1; i++) {
+ assertEquals("Check Bytes 1", i, bytes[i]);
+ }
+
+ // Read into array
+ int count2 = input.read(bytes);
+ assertEquals("Read 2", 5, count2);
+ for (int i = 0; i < count2; i++) {
+ assertEquals("Check Bytes 2", count1 + i, bytes[i]);
+ }
+
+ // End of File
+ int count3 = input.read(bytes);
+ assertEquals("Read 3 (EOF)", -1, count3);
+
+ // Test reading after the end of file
+ try {
+ int result = input.read(bytes);
+ assertEquals("Wrong value read after end of file", -1, result);
+ }
+ catch (IOException e) {
+ fail("Should not have thrown an IOException: " + e.getMessage());
+ }
+
+ // Reset
+ input = makeInputStream(data);
+
+ // Read into array using offset & length
+ int offset = 2;
+ int lth = 4;
+ int count5 = input.read(bytes, offset, lth);
+ assertEquals("Read 5", lth, count5);
+ for (int i = offset; i < lth; i++) {
+ assertEquals("Check Bytes 2", i - offset, bytes[i]);
+ }
+ }
+
+ @Test
+ public void testEOF() throws Exception {
+ InputStream input = makeInputStream(makeOrderedArray(2));
+ assertEquals("Read 1", 0, input.read());
+ assertEquals("Read 2", 1, input.read());
+ assertEquals("Read 3", -1, input.read());
+ assertEquals("Read 4", -1, input.read());
+ assertEquals("Read 5", -1, input.read());
+ }
+
+ @Test
+ public void testMarkResetUnsupported() throws IOException {
+ InputStream input = makeInputStream(10);
+ if (input.markSupported()) {
+ return;
+ }
+
+ input.mark(100); // Should be a no-op
+
+ int read = input.read();
+ assertTrue(read >= 0);
+
+ // TODO: According to InputStream#reset, it is allowed to do some
+ // implementation specific reset, and still be correct...
+ try {
+ input.reset();
+ fail("Should throw IOException");
+ }
+ catch (IOException e) {
+ assertTrue("Wrong messge: " + e.getMessage(), e.getMessage().contains("reset"));
+ }
+ }
+
+ @Test
+ public void testResetNoMark() throws Exception {
+ InputStream input = makeInputStream(makeOrderedArray(10));
+
+ if (!input.markSupported()) {
+ return; // Not supported, skip test
+ }
+
+ int read = input.read();
+ assertEquals(0, read);
+
+ // No mark may either throw exception, or reset to beginning of stream.
+ try {
+ input.reset();
+ assertEquals("Re-read of reset data should be same", 0, input.read());
+ }
+ catch (Exception e) {
+ assertTrue("Wrong no mark IOException message", e.getMessage().contains("mark"));
+ }
+ }
+
+ @Test
+ public void testMarkReset() throws Exception {
+ InputStream input = makeInputStream(makeOrderedArray(25));
+
+ if (!input.markSupported()) {
+ return; // Not supported, skip test
+ }
+
+ int read = input.read();
+ assertEquals(0, read);
+
+ int position = 1;
+ int readlimit = 10;
+
+ // Mark
+ input.mark(readlimit);
+
+ // Read further
+ for (int i = 0; i < 3; i++) {
+ assertEquals("Read After Mark [" + i + "]", (position + i), input.read());
+ }
+
+ // Reset
+ input.reset();
+
+ // Read from marked position
+ for (int i = 0; i < readlimit + 1; i++) {
+ assertEquals("Read After Reset [" + i + "]", (position + i), input.read());
+ }
+ }
+
+ @Test
+ public void testResetAfterReadLimit() throws Exception {
+ InputStream input = makeInputStream(makeOrderedArray(25));
+
+ if (!input.markSupported()) {
+ return; // Not supported, skip test
+ }
+
+ int read = input.read();
+ assertEquals(0, read);
+
+ int position = 1;
+ int readlimit = 5;
+
+ // Mark
+ input.mark(readlimit);
+
+ // Read past marked position
+ for (int i = 0; i < readlimit + 1; i++) {
+ assertEquals("Read After Reset [" + i + "]", (position + i), input.read());
+ }
+
+ // Reset after read limit passed, may either throw exception, or reset to last mark
+ try {
+ input.reset();
+ assertEquals("Re-read of reset data should be same", 1, input.read());
+ }
+ catch (Exception e) {
+ assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
+ }
+ }
+
+ @Test
+ public void testResetAfterReset() throws Exception {
+ InputStream input = makeInputStream(makeOrderedArray(25));
+
+ if (!input.markSupported()) {
+ return; // Not supported, skip test
+ }
+
+ int first = input.read();
+ assertTrue("Expected to read positive value", first >= 0);
+
+ int readlimit = 5;
+
+ // Mark
+ input.mark(readlimit);
+ int read = input.read();
+ assertTrue("Expected to read positive value", read >= 0);
+
+ assertTrue(input.read() >= 0);
+ assertTrue(input.read() >= 0);
+
+ input.reset();
+ assertEquals("Expected value read differs from actual", read, input.read());
+
+ // Reset after read limit passed, may either throw exception, or reset to last good mark
+ try {
+ input.reset();
+ int reRead = input.read();
+ assertTrue("Re-read of reset data should be same as initially marked or first", reRead == read || reRead == first);
+ }
+ catch (Exception e) {
+ assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
+ }
+ }
+
+ @Test
+ public void testSkip() throws Exception {
+ InputStream input = makeInputStream(makeOrderedArray(10));
+
+ assertEquals("Unexpected value read", 0, input.read());
+ assertEquals("Unexpected value read", 1, input.read());
+ assertEquals("Unexpected number of bytes skipped", 5, input.skip(5));
+ assertEquals("Unexpected value read", 7, input.read());
+
+ assertEquals("Unexpected number of bytes skipped", 2, input.skip(5)); // only 2 left to skip
+ assertEquals("Unexpected value read after EOF", -1, input.read());
+
+ // Spec says skip might return 0 or negative after EOF...
+ assertTrue("Positive value skipped after EOF", input.skip(5) <= 0); // End of file
+ assertEquals("Unexpected value read after EOF", -1, input.read());
+ }
+
+ @Test
+ public void testSanityOrdered() throws IOException {
+ // This is to sanity check that the test itself is correct...
+ byte[] bytes = makeOrderedArray(25);
+ InputStream expected = new ByteArrayInputStream(bytes);
+ InputStream actual = makeInputStream(bytes);
+
+ for (byte b : bytes) {
+ assertEquals((int) b, expected.read());
+ assertEquals((int) b, actual.read());
+ }
+ }
+
+ @Test
+ public void testSanityOrdered2() throws IOException {
+ // This is to sanity check that the test itself is correct...
+ byte[] bytes = makeOrderedArray(25);
+ InputStream expected = new ByteArrayInputStream(bytes);
+ InputStream actual = makeInputStream(bytes);
+
+ byte[] e = new byte[bytes.length];
+ byte[] a = new byte[bytes.length];
+
+ assertEquals(e.length, expected.read(e, 0, e.length));
+ assertEquals(a.length, actual.read(a, 0, a.length));
+
+ for (int i = 0; i < bytes.length; i++) {
+ assertEquals(bytes[i], e[i]);
+ assertEquals(bytes[i], a[i]);
+ }
+ }
+
+ @Test
+ public void testSanityNegative() throws IOException {
+ // This is to sanity check that the test itself is correct...
+ byte[] bytes = new byte[25];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = (byte) (255 - i);
+ }
+ InputStream expected = new ByteArrayInputStream(bytes);
+ InputStream actual = makeInputStream(bytes);
+
+ for (byte b : bytes) {
+ assertEquals(b & 0xff, expected.read());
+ assertEquals(b & 0xff, actual.read());
+ }
+ }
+
+ @Test
+ public void testSanityNegative2() throws IOException {
+ // This is to sanity check that the test itself is correct...
+ byte[] bytes = new byte[25];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = (byte) (255 - i);
+ }
+ InputStream expected = new ByteArrayInputStream(bytes);
+ InputStream actual = makeInputStream(bytes);
+
+ byte[] e = new byte[bytes.length];
+ byte[] a = new byte[bytes.length];
+
+ assertEquals(e.length, expected.read(e, 0, e.length));
+ assertEquals(a.length, actual.read(a, 0, a.length));
+
+ for (int i = 0; i < bytes.length; i++) {
+ assertEquals(bytes[i], e[i]);
+ assertEquals(bytes[i], a[i]);
+ }
+ }
+
+ @Test
+ public void testSanityRandom() throws IOException {
+ // This is to sanity check that the test itself is correct...
+ byte[] bytes = makeRandomArray(25);
+ InputStream expected = new ByteArrayInputStream(bytes);
+ InputStream actual = makeInputStream(bytes);
+
+ for (byte b : bytes) {
+ assertEquals(b & 0xff, expected.read());
+ assertEquals(b & 0xff, actual.read());
+ }
+ }
+
+ @Test
+ public void testSanityRandom2() throws IOException {
+ // This is to sanity check that the test itself is correct...
+ byte[] bytes = makeRandomArray(25);
+ InputStream expected = new ByteArrayInputStream(bytes);
+ InputStream actual = makeInputStream(bytes);
+
+ byte[] e = new byte[bytes.length];
+ byte[] a = new byte[bytes.length];
+
+ assertEquals(e.length, expected.read(e, 0, e.length));
+ assertEquals(a.length, actual.read(a, 0, a.length));
+
+ for (int i = 0; i < bytes.length; i++) {
+ assertEquals(bytes[i], e[i]);
+ assertEquals(bytes[i], a[i]);
+ }
+ }}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/LittleEndianDataInputStreamTest.java b/common/common-io/src/test/java/com/twelvemonkeys/io/LittleEndianDataInputStreamTest.java
index 1f7c551b..9527c85c 100644
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/LittleEndianDataInputStreamTest.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/LittleEndianDataInputStreamTest.java
@@ -4,26 +4,28 @@
*
* 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 "TwelveMonkeys" 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 OWNER 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.
+ * * 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;
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/MemoryCacheSeekableStreamTest.java b/common/common-io/src/test/java/com/twelvemonkeys/io/MemoryCacheSeekableStreamTest.java
new file mode 100755
index 00000000..f8e5d028
--- /dev/null
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/MemoryCacheSeekableStreamTest.java
@@ -0,0 +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.InputStream;
+
+/**
+ * MemoryCacheSeekableStreamTestCase
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/MemoryCacheSeekableStreamTestCase.java#2 $
+ */
+public class MemoryCacheSeekableStreamTest extends SeekableInputStreamAbstractTest {
+ protected SeekableInputStream makeInputStream(final InputStream pStream) {
+ return new MemoryCacheSeekableStream(pStream);
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/MemoryCacheSeekableStreamTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/MemoryCacheSeekableStreamTestCase.java
deleted file mode 100755
index 53387cb5..00000000
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/MemoryCacheSeekableStreamTestCase.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.twelvemonkeys.io;
-
-import java.io.InputStream;
-
-/**
- * MemoryCacheSeekableStreamTestCase
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/MemoryCacheSeekableStreamTestCase.java#2 $
- */
-public class MemoryCacheSeekableStreamTestCase extends SeekableInputStreamAbstractTestCase {
- protected SeekableInputStream makeInputStream(final InputStream pStream) {
- return new MemoryCacheSeekableStream(pStream);
- }
-}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/OutputStreamAbstractTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/OutputStreamAbstractTest.java
similarity index 79%
rename from common/common-io/src/test/java/com/twelvemonkeys/io/OutputStreamAbstractTestCase.java
rename to common/common-io/src/test/java/com/twelvemonkeys/io/OutputStreamAbstractTest.java
index d3405dae..c5f5a412 100755
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/OutputStreamAbstractTestCase.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/OutputStreamAbstractTest.java
@@ -1,254 +1,285 @@
-package com.twelvemonkeys.io;
-
-import com.twelvemonkeys.lang.ObjectAbstractTestCase;
-import org.junit.Test;
-
-import java.io.OutputStream;
-import java.io.IOException;
-
-import static org.junit.Assert.*;
-
-/**
- * InputStreamAbstractTestCase
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/OutputStreamAbstractTestCase.java#1 $
- */
-public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCase {
- protected abstract OutputStream makeObject();
-
- @Test
- public void testWrite() throws IOException {
- OutputStream os = makeObject();
-
- for (int i = 0; i < 256; i++) {
- os.write((byte) i);
- }
- }
-
- @Test
- public void testWriteByteArray() throws IOException {
- OutputStream os = makeObject();
-
- os.write(new byte[256]);
- }
-
- @Test
- public void testWriteByteArrayNull() {
- OutputStream os = makeObject();
- try {
- os.write(null);
- fail("Should not accept null-argument");
- }
- catch (IOException e) {
- fail("Should not throw IOException of null-arguemnt: " + e.getMessage());
- }
- catch (NullPointerException e) {
- assertNotNull(e);
- }
- catch (RuntimeException e) {
- fail("Should only throw NullPointerException: " + e.getClass() + ": " + e.getMessage());
- }
- }
-
- @Test
- public void testWriteByteArrayOffsetLength() throws IOException {
- byte[] input = new byte[256];
-
- OutputStream os = makeObject();
-
- // TODO: How to test that data is actually written!?
- for (int i = 0; i < 256; i++) {
- input[i] = (byte) i;
- }
-
- for (int i = 0; i < 256; i++) {
- os.write(input, i, 256 - i);
- }
-
- for (int i = 0; i < 4; i++) {
- os.write(input, i * 64, 64);
- }
- }
-
- @Test
- public void testWriteByteArrayZeroLength() {
- OutputStream os = makeObject();
- try {
- os.write(new byte[1], 0, 0);
- }
- catch (Exception e) {
- fail("Should not throw Exception: " + e.getMessage());
- }
- }
-
- @Test
- public void testWriteByteArrayOffsetLengthNull() {
- OutputStream os = makeObject();
- try {
- os.write(null, 5, 10);
- fail("Should not accept null-argument");
- }
- catch (IOException e) {
- fail("Should not throw IOException of null-arguemnt: " + e.getMessage());
- }
- catch (NullPointerException e) {
- assertNotNull(e);
- }
- catch (RuntimeException e) {
- fail("Should only throw NullPointerException: " + e.getClass() + ": " + e.getMessage());
- }
- }
-
- @Test
- public void testWriteByteArrayNegativeOffset() {
- OutputStream os = makeObject();
- try {
- os.write(new byte[5], -3, 5);
- fail("Should not accept negative offset");
- }
- catch (IOException e) {
- fail("Should not throw IOException negative offset: " + e.getMessage());
- }
- catch (IndexOutOfBoundsException e) {
- assertNotNull(e);
- }
- catch (RuntimeException e) {
- fail("Should only throw IndexOutOfBoundsException: " + e.getClass() + ": " + e.getMessage());
- }
- }
-
- @Test
- public void testWriteByteArrayNegativeLength() {
- OutputStream os = makeObject();
- try {
- os.write(new byte[5], 2, -5);
- fail("Should not accept negative length");
- }
- catch (IOException e) {
- fail("Should not throw IOException negative length: " + e.getMessage());
- }
- catch (IndexOutOfBoundsException e) {
- assertNotNull(e);
- }
- catch (RuntimeException e) {
- fail("Should only throw IndexOutOfBoundsException: " + e.getClass() + ": " + e.getMessage());
- }
- }
-
- @Test
- public void testWriteByteArrayOffsetOutOfBounds() {
- OutputStream os = makeObject();
- try {
- os.write(new byte[5], 5, 1);
- fail("Should not accept offset out of bounds");
- }
- catch (IOException e) {
- fail("Should not throw IOException offset out of bounds: " + e.getMessage());
- }
- catch (IndexOutOfBoundsException e) {
- assertNotNull(e);
- }
- catch (RuntimeException e) {
- fail("Should only throw IndexOutOfBoundsException: " + e.getClass() + ": " + e.getMessage());
- }
- }
-
- @Test
- public void testWriteByteArrayLengthOutOfBounds() {
- OutputStream os = makeObject();
- try {
- os.write(new byte[5], 1, 5);
- fail("Should not accept length out of bounds");
- }
- catch (IOException e) {
- fail("Should not throw IOException length out of bounds: " + e.getMessage());
- }
- catch (IndexOutOfBoundsException e) {
- assertNotNull(e);
- }
- catch (RuntimeException e) {
- fail("Should only throw IndexOutOfBoundsException: " + e.getClass() + ": " + e.getMessage());
- }
- }
-
- @Test
- public void testFlush() {
- // TODO: Implement
- }
-
- @Test
- public void testClose() {
- // TODO: Implement
- }
-
- @Test
- public void testWriteAfterClose() throws IOException {
- OutputStream os = makeObject();
-
- os.close();
-
- boolean success = false;
- try {
- os.write(0);
- success = true;
- // TODO: Not all streams throw exception! (ByteArrayOutputStream)
- //fail("Write after close");
- }
- catch (IOException e) {
- assertNotNull(e.getMessage());
- }
-
- try {
- os.write(new byte[16]);
- // TODO: Not all streams throw exception! (ByteArrayOutputStream)
- //fail("Write after close");
- if (!success) {
- fail("Inconsistent write(int)/write(byte[]) after close");
- }
- }
- catch (IOException e) {
- assertNotNull(e.getMessage());
- if (success) {
- fail("Inconsistent write(int)/write(byte[]) after close");
- }
- }
- }
-
- @Test
- public void testFlushAfterClose() throws IOException {
- OutputStream os = makeObject();
-
- os.close();
-
- try {
- os.flush();
- // TODO: Not all streams throw exception! (ByteArrayOutputStream)
- //fail("Flush after close");
- try {
- os.write(0);
- }
- catch (IOException e) {
- fail("Inconsistent write/flush after close");
- }
- }
- catch (IOException e) {
- assertNotNull(e.getMessage());
- }
- }
-
- @Test
- public void testCloseAfterClose() throws IOException {
- OutputStream os = makeObject();
-
- os.close();
-
- try {
- os.close();
- }
- catch (IOException e) {
- fail("Close after close, failed: " + e.getMessage());
- }
- }
-}
+/*
+ * 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.ObjectAbstractTest;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+/**
+ * InputStreamAbstractTestCase
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/OutputStreamAbstractTestCase.java#1 $
+ */
+public abstract class OutputStreamAbstractTest extends ObjectAbstractTest {
+ protected abstract OutputStream makeObject();
+
+ @Test
+ public void testWrite() throws IOException {
+ OutputStream os = makeObject();
+
+ for (int i = 0; i < 256; i++) {
+ os.write((byte) i);
+ }
+ }
+
+ @Test
+ public void testWriteByteArray() throws IOException {
+ OutputStream os = makeObject();
+
+ os.write(new byte[256]);
+ }
+
+ @Test
+ public void testWriteByteArrayNull() {
+ OutputStream os = makeObject();
+ try {
+ os.write(null);
+ fail("Should not accept null-argument");
+ }
+ catch (IOException e) {
+ fail("Should not throw IOException of null-arguemnt: " + e.getMessage());
+ }
+ catch (NullPointerException e) {
+ assertNotNull(e);
+ }
+ catch (RuntimeException e) {
+ fail("Should only throw NullPointerException: " + e.getClass() + ": " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testWriteByteArrayOffsetLength() throws IOException {
+ byte[] input = new byte[256];
+
+ OutputStream os = makeObject();
+
+ // TODO: How to test that data is actually written!?
+ for (int i = 0; i < 256; i++) {
+ input[i] = (byte) i;
+ }
+
+ for (int i = 0; i < 256; i++) {
+ os.write(input, i, 256 - i);
+ }
+
+ for (int i = 0; i < 4; i++) {
+ os.write(input, i * 64, 64);
+ }
+ }
+
+ @Test
+ public void testWriteByteArrayZeroLength() {
+ OutputStream os = makeObject();
+ try {
+ os.write(new byte[1], 0, 0);
+ }
+ catch (Exception e) {
+ fail("Should not throw Exception: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testWriteByteArrayOffsetLengthNull() {
+ OutputStream os = makeObject();
+ try {
+ os.write(null, 5, 10);
+ fail("Should not accept null-argument");
+ }
+ catch (IOException e) {
+ fail("Should not throw IOException of null-arguemnt: " + e.getMessage());
+ }
+ catch (NullPointerException e) {
+ assertNotNull(e);
+ }
+ catch (RuntimeException e) {
+ fail("Should only throw NullPointerException: " + e.getClass() + ": " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testWriteByteArrayNegativeOffset() {
+ OutputStream os = makeObject();
+ try {
+ os.write(new byte[5], -3, 5);
+ fail("Should not accept negative offset");
+ }
+ catch (IOException e) {
+ fail("Should not throw IOException negative offset: " + e.getMessage());
+ }
+ catch (IndexOutOfBoundsException e) {
+ assertNotNull(e);
+ }
+ catch (RuntimeException e) {
+ fail("Should only throw IndexOutOfBoundsException: " + e.getClass() + ": " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testWriteByteArrayNegativeLength() {
+ OutputStream os = makeObject();
+ try {
+ os.write(new byte[5], 2, -5);
+ fail("Should not accept negative length");
+ }
+ catch (IOException e) {
+ fail("Should not throw IOException negative length: " + e.getMessage());
+ }
+ catch (IndexOutOfBoundsException e) {
+ assertNotNull(e);
+ }
+ catch (RuntimeException e) {
+ fail("Should only throw IndexOutOfBoundsException: " + e.getClass() + ": " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testWriteByteArrayOffsetOutOfBounds() {
+ OutputStream os = makeObject();
+ try {
+ os.write(new byte[5], 5, 1);
+ fail("Should not accept offset out of bounds");
+ }
+ catch (IOException e) {
+ fail("Should not throw IOException offset out of bounds: " + e.getMessage());
+ }
+ catch (IndexOutOfBoundsException e) {
+ assertNotNull(e);
+ }
+ catch (RuntimeException e) {
+ fail("Should only throw IndexOutOfBoundsException: " + e.getClass() + ": " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testWriteByteArrayLengthOutOfBounds() {
+ OutputStream os = makeObject();
+ try {
+ os.write(new byte[5], 1, 5);
+ fail("Should not accept length out of bounds");
+ }
+ catch (IOException e) {
+ fail("Should not throw IOException length out of bounds: " + e.getMessage());
+ }
+ catch (IndexOutOfBoundsException e) {
+ assertNotNull(e);
+ }
+ catch (RuntimeException e) {
+ fail("Should only throw IndexOutOfBoundsException: " + e.getClass() + ": " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testFlush() {
+ // TODO: Implement
+ }
+
+ @Test
+ public void testClose() {
+ // TODO: Implement
+ }
+
+ @Test
+ public void testWriteAfterClose() throws IOException {
+ OutputStream os = makeObject();
+
+ os.close();
+
+ boolean success = false;
+ try {
+ os.write(0);
+ success = true;
+ // TODO: Not all streams throw exception! (ByteArrayOutputStream)
+ //fail("Write after close");
+ }
+ catch (IOException e) {
+ assertNotNull(e.getMessage());
+ }
+
+ try {
+ os.write(new byte[16]);
+ // TODO: Not all streams throw exception! (ByteArrayOutputStream)
+ //fail("Write after close");
+ if (!success) {
+ fail("Inconsistent write(int)/write(byte[]) after close");
+ }
+ }
+ catch (IOException e) {
+ assertNotNull(e.getMessage());
+ if (success) {
+ fail("Inconsistent write(int)/write(byte[]) after close");
+ }
+ }
+ }
+
+ @Test
+ public void testFlushAfterClose() throws IOException {
+ OutputStream os = makeObject();
+
+ os.close();
+
+ try {
+ os.flush();
+ // TODO: Not all streams throw exception! (ByteArrayOutputStream)
+ //fail("Flush after close");
+ try {
+ os.write(0);
+ }
+ catch (IOException e) {
+ fail("Inconsistent write/flush after close");
+ }
+ }
+ catch (IOException e) {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testCloseAfterClose() throws IOException {
+ OutputStream os = makeObject();
+
+ os.close();
+
+ try {
+ os.close();
+ }
+ catch (IOException e) {
+ fail("Close after close, failed: " + e.getMessage());
+ }
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/ReaderAbstractTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/ReaderAbstractTest.java
similarity index 79%
rename from common/common-io/src/test/java/com/twelvemonkeys/io/ReaderAbstractTestCase.java
rename to common/common-io/src/test/java/com/twelvemonkeys/io/ReaderAbstractTest.java
index cf9e63d5..8fc72aa6 100755
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/ReaderAbstractTestCase.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/ReaderAbstractTest.java
@@ -1,224 +1,254 @@
-package com.twelvemonkeys.io;
-
-import com.twelvemonkeys.lang.ObjectAbstractTestCase;
-import org.junit.Test;
-
-import java.io.Reader;
-import java.io.IOException;
-
-import static org.junit.Assert.*;
-
-/**
- * ReaderAbstractTestCase
- *
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/ReaderAbstractTestCase.java#1 $
- */
-public abstract class ReaderAbstractTestCase extends ObjectAbstractTestCase {
-
- // Kindly provided by lipsum.org :-)
- protected final String mInput =
- "Cras tincidunt euismod tellus. Aenean a odio. " +
- "Aenean metus. Sed tristique est non purus. Class aptent " +
- "taciti sociosqu ad litora torquent per conubia nostra, per " +
- "inceptos hymenaeos. Fusce vulputate dolor non mauris. " +
- "Nullam nunc massa, pretium quis, ultricies a, varius quis, " +
- "neque. Nam id nulla eu ante malesuada fermentum. Sed " +
- "vulputate purus eget magna. Sed mollis. Curabitur enim " +
- "diam, faucibus ac, hendrerit eu, consequat nec, augue.";
-
- protected final Object makeObject() {
- return makeReader();
- }
-
- protected Reader makeReader() {
- return makeReader(mInput);
- }
-
- protected abstract Reader makeReader(String pInput);
-
- @Test
- public void testRead() throws IOException {
- Reader reader = makeReader();
-
- int count = 0;
- int ch;
- StringBuilder buffer = new StringBuilder(mInput.length());
- while ((ch = reader.read()) > 0) {
- count++;
- buffer.append((char) ch);
- }
-
- assertEquals(mInput.length(), count);
- assertEquals(mInput, buffer.toString());
- }
-
- @Test
- public void testReadBuffer() throws IOException {
- Reader reader = makeReader();
-
- char[] chars = new char[mInput.length()];
- StringBuilder buffer = new StringBuilder(mInput.length());
-
- int count;
- int offset = 0;
- int lenght = chars.length;
- while ((count = reader.read(chars, offset, lenght)) > 0) {
- buffer.append(chars, offset, count);
- offset += count;
- lenght -= count;
- }
-
- assertEquals(mInput, buffer.toString());
- assertEquals(mInput, new String(chars));
- }
-
- @Test
- public void testSkipToEnd() throws IOException {
- Reader reader = makeReader();
-
- int toSkip = mInput.length();
- while (toSkip > 0) {
- long skipped = reader.skip(toSkip);
- assertFalse("Skipped < 0", skipped < 0);
- toSkip -= skipped;
- }
-
- assertEquals(0, toSkip);
- }
-
- @Test
- public void testSkipToEndAndRead() throws IOException {
- Reader reader = makeReader();
-
- int toSkip = mInput.length();
- while (toSkip > 0) {
- toSkip -= reader.skip(toSkip);
- }
-
- assertEquals(reader.read(), -1);
- }
-
- // TODO: It's possible to support reset and not mark (resets to beginning of stream, for example)
- @Test
- public void testResetMarkSupported() throws IOException {
- Reader reader = makeReader();
-
- if (reader.markSupported()) {
- // Mark at 0
- reader.mark(mInput.length() / 4);
-
- // Read one char
- char ch = (char) reader.read();
- reader.reset();
- assertEquals(ch, (char) reader.read());
- reader.reset();
-
- // Read from start
- StringBuilder first = new StringBuilder(mInput.length() / 4);
- for (int i = 0; i < mInput.length() / 4; i++) {
- first.append((char) reader.read());
- }
-
- reader.reset(); // 0
-
- StringBuilder second = new StringBuilder(mInput.length() / 4);
- for (int i = 0; i < mInput.length() / 4; i++) {
- second.append((char) reader.read());
- }
-
- assertEquals(first.toString(), second.toString());
-
- // Mark at 1/4
- reader.mark(mInput.length() / 4);
-
- // Read from 1/4
- first = new StringBuilder(mInput.length() / 4);
- for (int i = 0; i < mInput.length() / 4; i++) {
- first.append((char) reader.read());
- }
-
- reader.reset(); // 1/4
-
- second = new StringBuilder(mInput.length() / 4);
- for (int i = 0; i < mInput.length() / 4; i++) {
- second.append((char) reader.read());
- }
-
- assertEquals(first.toString(), second.toString());
-
- // Read past limit
- reader.read();
-
- // This may or may not fail, depending on the stream
- try {
- reader.reset();
- }
- catch (IOException ioe) {
- assertNotNull(ioe.getMessage());
- }
- }
- }
-
- @Test
- public void testResetMarkNotSupported() throws IOException {
- Reader reader = makeReader();
-
- if (!reader.markSupported()) {
- try {
- reader.mark(mInput.length());
- fail("Mark set, while markSupprted is false");
- }
- catch (IOException e) {
- assertNotNull(e.getMessage());
- }
-
- // Read one char
- char ch = (char) reader.read();
- try {
- reader.reset();
- assertEquals(ch, (char) reader.read());
- }
- catch (IOException ioe) {
- assertNotNull(ioe.getMessage());
- }
-
- // Read from start
- StringBuilder first = new StringBuilder(mInput.length() / 4);
- for (int i = 0; i < mInput.length() / 4; i++) {
- first.append((char) reader.read());
- }
-
- try {
- reader.reset(); // 0
-
- StringBuilder second = new StringBuilder(mInput.length() / 4);
- for (int i = 0; i < mInput.length() / 4; i++) {
- second.append((char) reader.read());
- }
-
- assertEquals(first.toString(), second.toString());
- }
- catch (IOException ioe) {
- assertNotNull(ioe.getMessage());
- }
- }
- }
-
- @Test
- public void testReadAfterClose() throws IOException {
- Reader reader = makeReader("foo bar");
-
- reader.close();
-
- try {
- reader.read();
- fail("Should not allow read after close");
- }
- catch (IOException ioe) {
- assertNotNull(ioe.getMessage());
- }
- }
-}
+/*
+ * 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.ObjectAbstractTest;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import static org.junit.Assert.*;
+
+/**
+ * ReaderAbstractTestCase
+ *
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/ReaderAbstractTestCase.java#1 $
+ */
+public abstract class ReaderAbstractTest extends ObjectAbstractTest {
+
+ // Kindly provided by lipsum.org :-)
+ protected final String mInput =
+ "Cras tincidunt euismod tellus. Aenean a odio. " +
+ "Aenean metus. Sed tristique est non purus. Class aptent " +
+ "taciti sociosqu ad litora torquent per conubia nostra, per " +
+ "inceptos hymenaeos. Fusce vulputate dolor non mauris. " +
+ "Nullam nunc massa, pretium quis, ultricies a, varius quis, " +
+ "neque. Nam id nulla eu ante malesuada fermentum. Sed " +
+ "vulputate purus eget magna. Sed mollis. Curabitur enim " +
+ "diam, faucibus ac, hendrerit eu, consequat nec, augue.";
+
+ protected final Object makeObject() {
+ return makeReader();
+ }
+
+ protected Reader makeReader() {
+ return makeReader(mInput);
+ }
+
+ protected abstract Reader makeReader(String pInput);
+
+ @Test
+ public void testRead() throws IOException {
+ Reader reader = makeReader();
+
+ int count = 0;
+ int ch;
+ StringBuilder buffer = new StringBuilder(mInput.length());
+ while ((ch = reader.read()) > 0) {
+ count++;
+ buffer.append((char) ch);
+ }
+
+ assertEquals(mInput.length(), count);
+ assertEquals(mInput, buffer.toString());
+ }
+
+ @Test
+ public void testReadBuffer() throws IOException {
+ Reader reader = makeReader();
+
+ char[] chars = new char[mInput.length()];
+ StringBuilder buffer = new StringBuilder(mInput.length());
+
+ int count;
+ int offset = 0;
+ int lenght = chars.length;
+ while ((count = reader.read(chars, offset, lenght)) > 0) {
+ buffer.append(chars, offset, count);
+ offset += count;
+ lenght -= count;
+ }
+
+ assertEquals(mInput, buffer.toString());
+ assertEquals(mInput, new String(chars));
+ }
+
+ @Test
+ public void testSkipToEnd() throws IOException {
+ Reader reader = makeReader();
+
+ int toSkip = mInput.length();
+ while (toSkip > 0) {
+ long skipped = reader.skip(toSkip);
+ assertFalse("Skipped < 0", skipped < 0);
+ toSkip -= skipped;
+ }
+
+ assertEquals(0, toSkip);
+ }
+
+ @Test
+ public void testSkipToEndAndRead() throws IOException {
+ Reader reader = makeReader();
+
+ int toSkip = mInput.length();
+ while (toSkip > 0) {
+ toSkip -= reader.skip(toSkip);
+ }
+
+ assertEquals(reader.read(), -1);
+ }
+
+ // TODO: It's possible to support reset and not mark (resets to beginning of stream, for example)
+ @Test
+ public void testResetMarkSupported() throws IOException {
+ Reader reader = makeReader();
+
+ if (reader.markSupported()) {
+ // Mark at 0
+ reader.mark(mInput.length() / 4);
+
+ // Read one char
+ char ch = (char) reader.read();
+ reader.reset();
+ assertEquals(ch, (char) reader.read());
+ reader.reset();
+
+ // Read from start
+ StringBuilder first = new StringBuilder(mInput.length() / 4);
+ for (int i = 0; i < mInput.length() / 4; i++) {
+ first.append((char) reader.read());
+ }
+
+ reader.reset(); // 0
+
+ StringBuilder second = new StringBuilder(mInput.length() / 4);
+ for (int i = 0; i < mInput.length() / 4; i++) {
+ second.append((char) reader.read());
+ }
+
+ assertEquals(first.toString(), second.toString());
+
+ // Mark at 1/4
+ reader.mark(mInput.length() / 4);
+
+ // Read from 1/4
+ first = new StringBuilder(mInput.length() / 4);
+ for (int i = 0; i < mInput.length() / 4; i++) {
+ first.append((char) reader.read());
+ }
+
+ reader.reset(); // 1/4
+
+ second = new StringBuilder(mInput.length() / 4);
+ for (int i = 0; i < mInput.length() / 4; i++) {
+ second.append((char) reader.read());
+ }
+
+ assertEquals(first.toString(), second.toString());
+
+ // Read past limit
+ reader.read();
+
+ // This may or may not fail, depending on the stream
+ try {
+ reader.reset();
+ }
+ catch (IOException ioe) {
+ assertNotNull(ioe.getMessage());
+ }
+ }
+ }
+
+ @Test
+ public void testResetMarkNotSupported() throws IOException {
+ Reader reader = makeReader();
+
+ if (!reader.markSupported()) {
+ try {
+ reader.mark(mInput.length());
+ fail("Mark set, while markSupprted is false");
+ }
+ catch (IOException e) {
+ assertNotNull(e.getMessage());
+ }
+
+ // Read one char
+ char ch = (char) reader.read();
+ try {
+ reader.reset();
+ assertEquals(ch, (char) reader.read());
+ }
+ catch (IOException ioe) {
+ assertNotNull(ioe.getMessage());
+ }
+
+ // Read from start
+ StringBuilder first = new StringBuilder(mInput.length() / 4);
+ for (int i = 0; i < mInput.length() / 4; i++) {
+ first.append((char) reader.read());
+ }
+
+ try {
+ reader.reset(); // 0
+
+ StringBuilder second = new StringBuilder(mInput.length() / 4);
+ for (int i = 0; i < mInput.length() / 4; i++) {
+ second.append((char) reader.read());
+ }
+
+ assertEquals(first.toString(), second.toString());
+ }
+ catch (IOException ioe) {
+ assertNotNull(ioe.getMessage());
+ }
+ }
+ }
+
+ @Test
+ public void testReadAfterClose() throws IOException {
+ Reader reader = makeReader("foo bar");
+
+ reader.close();
+
+ try {
+ reader.read();
+ fail("Should not allow read after close");
+ }
+ catch (IOException ioe) {
+ assertNotNull(ioe.getMessage());
+ }
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableAbstractTest.java b/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableAbstractTest.java
new file mode 100755
index 00000000..2ae256ec
--- /dev/null
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableAbstractTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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 org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * SeekableAbstractTestCase
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/SeekableAbstractTestCase.java#1 $
+ */
+public abstract class SeekableAbstractTest implements SeekableInterfaceTest {
+
+ protected abstract Seekable createSeekable();
+
+ @Test
+ public void testFail() {
+ fail("Do not create stand-alone test classes based on this class. Instead, create an inner class and delegate to it.");
+ }
+
+ @Test
+ public void testSeekable() {
+ assertTrue(createSeekable() instanceof Seekable);
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableAbstractTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableAbstractTestCase.java
deleted file mode 100755
index 28dfb036..00000000
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableAbstractTestCase.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.twelvemonkeys.io;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * SeekableAbstractTestCase
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/SeekableAbstractTestCase.java#1 $
- */
-public abstract class SeekableAbstractTestCase implements SeekableInterfaceTest {
-
- protected abstract Seekable createSeekable();
-
- @Test
- public void testFail() {
- fail("Do not create stand-alone test classes based on this class. Instead, create an inner class and delegate to it.");
- }
-
- @Test
- public void testSeekable() {
- assertTrue(createSeekable() instanceof Seekable);
- }
-}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableInputStreamAbstractTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableInputStreamAbstractTest.java
similarity index 90%
rename from common/common-io/src/test/java/com/twelvemonkeys/io/SeekableInputStreamAbstractTestCase.java
rename to common/common-io/src/test/java/com/twelvemonkeys/io/SeekableInputStreamAbstractTest.java
index 7a995e49..4a4b2d25 100755
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableInputStreamAbstractTestCase.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableInputStreamAbstractTest.java
@@ -1,488 +1,518 @@
-package com.twelvemonkeys.io;
-
-import org.junit.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-
-import static org.junit.Assert.*;
-
-/**
- * SeekableInputStreamAbstractTestCase
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/SeekableInputStreamAbstractTestCase.java#4 $
- */
-public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbstractTestCase implements SeekableInterfaceTest {
- //// TODO: Figure out a better way of creating interface tests without duplicating code
- final SeekableAbstractTestCase seekableTestCase = new SeekableAbstractTestCase() {
- protected Seekable createSeekable() {
- return makeInputStream();
- }
- };
-
- @Override
- protected SeekableInputStream makeInputStream() {
- return (SeekableInputStream) super.makeInputStream();
- }
-
- @Override
- protected SeekableInputStream makeInputStream(final int pSize) {
- return (SeekableInputStream) super.makeInputStream(pSize);
- }
-
- protected SeekableInputStream makeInputStream(byte[] pBytes) {
- return makeInputStream(new ByteArrayInputStream(pBytes));
- }
-
- protected abstract SeekableInputStream makeInputStream(InputStream pStream);
-
- @Test
- @Override
- public void testResetAfterReset() throws Exception {
- InputStream input = makeInputStream(makeOrderedArray(25));
-
- if (!input.markSupported()) {
- return; // Not supported, skip test
- }
-
- assertTrue("Expected to read positive value", input.read() >= 0);
-
- int readlimit = 5;
-
- // Mark
- input.mark(readlimit);
- int read = input.read();
- assertTrue("Expected to read positive value", read >= 0);
-
- input.reset();
- assertEquals("Expected value read differs from actual", read, input.read());
-
- // Reset after read limit passed, may either throw exception, or reset to last good mark
- try {
- input.reset();
- assertEquals("Re-read of reset data should be first", 0, input.read());
- }
- catch (Exception e) {
- assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
- }
- }
-
- @Test
- public void testSeekable() {
- seekableTestCase.testSeekable();
- }
-
- @Test
- public void testFlushBeyondCurrentPos() throws Exception {
- SeekableInputStream seekable = makeInputStream(20);
-
- int pos = 10;
- try {
- seekable.flushBefore(pos);
- fail("Flush beyond current position should throw IndexOutOfBoundsException");
- }
- catch (IndexOutOfBoundsException e) {
- // Ignore
- }
- }
-
- @Test
- public void testSeek() throws Exception {
- SeekableInputStream seekable = makeInputStream(55);
- int pos = 37;
-
- seekable.seek(pos);
- long streamPos = seekable.getStreamPosition();
- assertEquals("Stream positon should match seeked position", pos, streamPos);
- }
-
- @Test
- public void testSeekFlush() throws Exception {
- SeekableInputStream seekable = makeInputStream(133);
- int pos = 45;
- seekable.seek(pos);
- seekable.flushBefore(pos);
- long flushedPos = seekable.getFlushedPosition();
- assertEquals("Flushed positon should match position", pos, flushedPos);
-
- try {
- seekable.seek(pos - 1);
- fail("Read before flushed position succeeded");
- }
- catch (IndexOutOfBoundsException e) {
- // Ignore
- }
- }
-
- @Test
- public void testMarkFlushReset() throws Exception {
- SeekableInputStream seekable = makeInputStream(77);
-
- seekable.mark();
-
- int position = 55;
- seekable.seek(position);
- seekable.flushBefore(position);
-
- try {
- seekable.reset();
- fail("Reset before flushed position succeeded");
- }
- catch (IOException e) {
- // Ignore
- }
-
- assertEquals(position, seekable.getStreamPosition());
- }
-
- @Test
- public void testSeekSkipRead() throws Exception {
- SeekableInputStream seekable = makeInputStream(133);
- int pos = 45;
- for (int i = 0; i < 10; i++) {
- seekable.seek(pos);
- //noinspection ResultOfMethodCallIgnored
- seekable.skip(i);
- byte[] bytes = FileUtil.read(seekable);
- assertEquals(133, seekable.getStreamPosition());
- assertEquals(133 - 45- i, bytes.length);
- }
- }
-
- protected void testSeekSkip(SeekableInputStream pSeekable, String pStr) throws IOException {
- System.out.println();
- pSeekable.seek(pStr.length());
- FileUtil.read(pSeekable);
- for (int i = 0; i < 10; i++) {
- byte[] bytes = FileUtil.read(pSeekable);
- int len = bytes.length;
- if (len != 0) {
- System.err.println("Error in buffer length after full read...");
- System.err.println("len: " + len);
- System.err.println("bytes: \"" + new String(bytes) + "\"");
- break;
- }
- }
-
- System.out.println();
-
- for (int i = 0; i < 10; i++) {
- pSeekable.seek(0);
- int skip = i * 3;
- //noinspection ResultOfMethodCallIgnored
- pSeekable.skip(skip);
- String str = new String(FileUtil.read(pSeekable));
- System.out.println(str);
- if (str.length() != pStr.length() - skip) {
- throw new Error("Error in buffer length after skip");
- }
- }
-
- System.out.println();
- System.out.println("seek/skip ok!");
- System.out.println();
- }
-
- protected static void markReset(SeekableInputStream pSeekable) throws IOException {
- for (int i = 0; i < 10; i++) {
- pSeekable.mark();
- System.out.println(new String(FileUtil.read(pSeekable)));
- pSeekable.reset();
- }
-
- System.out.println();
- System.out.println("mark/reset ok!");
- }
-
- protected static void timeRead(SeekableInputStream pSeekable) throws IOException {
- for (int i = 0; i < 5000; i++) {
- pSeekable.mark();
- FileUtil.read(pSeekable);
- pSeekable.reset();
- }
-
- long start = System.currentTimeMillis();
- final int times = 200000;
- for (int i = 0; i < times; i++) {
- pSeekable.mark();
- FileUtil.read(pSeekable);
- pSeekable.reset();
- }
- long time = System.currentTimeMillis() - start;
-
- System.out.println("Time; " + time + "ms (" + (time / (float) times) + "ms/inv)");
- }
-
- /*
-
- // Test code below...
- protected final static String STR = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Fusce massa orci, adipiscing vel, dapibus et, vulputate tristique, tortor. Quisque sodales. Mauris varius turpis et pede. Nam ac dolor vel diam condimentum elementum. Pellentesque eget tellus. Praesent magna. Sed fringilla. Proin ullamcorper tincidunt ante. Fusce dapibus nibh nec dolor. Etiam erat. Nullam dignissim laoreet nibh. Maecenas scelerisque. Pellentesque in quam. Maecenas sollicitudin, magna nec imperdiet facilisis, metus quam tristique ipsum, vitae consequat massa purus eget leo. Nulla ipsum. Proin non purus eget tellus lobortis iaculis. In lorem justo, posuere id, vulputate at, adipiscing ut, nisl. Nunc dui erat, tincidunt ac, interdum quis, rutrum et, libero. Etiam lectus dui, viverra sit amet, elementum ut, malesuada sed, massa. Vestibulum mi nulla, sodales vel, vestibulum sed, congue blandit, velit.";
-
- protected static void flushSeek(SeekableInputStream pSeekable, String pStr) throws IOException {
- pSeekable.seek(0);
- pSeekable.mark();
- int pos = pStr.length() / 2;
- try {
- pSeekable.flushBefore(pos);
- System.err.println("Error in flush/seek");
- }
- catch (IndexOutOfBoundsException e) {
- // Ignore
- }
- pSeekable.seek(pos);
- long streamPos = pSeekable.getStreamPosition();
- if (streamPos != pos) {
- System.err.println("Streampos not equal seeked pos");
- }
-
- pSeekable.flushBefore(pos);
- long flushedPos = pSeekable.getFlushedPosition();
- if (flushedPos != pos) {
- System.err.println("flushedpos not equal set flushed pos");
- }
-
- for (int i = 0; i < 10; i++) {
- pSeekable.seek(pos);
- //noinspection ResultOfMethodCallIgnored
- pSeekable.skip(i);
- System.out.println(new String(FileUtil.read(pSeekable)));
- }
-
- try {
- pSeekable.seek(pos - 1);
- System.err.println("Error in flush/seek");
- }
- catch (IndexOutOfBoundsException e) {
- // Ignore
- }
- try {
- pSeekable.reset();
- System.err.println("Error in flush/seek");
- }
- catch (IOException e) {
- // Ignore
- }
-
- System.out.println();
- System.out.println("flush/seek ok!");
- }
-
- protected static void seekSkip(SeekableInputStream pSeekable, String pStr) throws IOException {
- System.out.println();
- pSeekable.seek(pStr.length());
- FileUtil.read(pSeekable);
- for (int i = 0; i < 10; i++) {
- byte[] bytes = FileUtil.read(pSeekable);
- int len = bytes.length;
- if (len != 0) {
- System.err.println("Error in buffer length after full read...");
- System.err.println("len: " + len);
- System.err.println("bytes: \"" + new String(bytes) + "\"");
- break;
- }
- }
-
- System.out.println();
-
- for (int i = 0; i < 10; i++) {
- pSeekable.seek(0);
- int skip = i * 3;
- //noinspection ResultOfMethodCallIgnored
- pSeekable.skip(skip);
- String str = new String(FileUtil.read(pSeekable));
- System.out.println(str);
- if (str.length() != pStr.length() - skip) {
- throw new Error("Error in buffer length after skip");
- }
- }
-
- System.out.println();
- System.out.println("seek/skip ok!");
- System.out.println();
- }
-
- protected static void markReset(SeekableInputStream pSeekable) throws IOException {
- for (int i = 0; i < 10; i++) {
- pSeekable.mark();
- System.out.println(new String(FileUtil.read(pSeekable)));
- pSeekable.reset();
- }
-
- System.out.println();
- System.out.println("mark/reset ok!");
- }
-
- protected static void timeRead(SeekableInputStream pSeekable) throws IOException {
- for (int i = 0; i < 5000; i++) {
- pSeekable.mark();
- FileUtil.read(pSeekable);
- pSeekable.reset();
- }
-
- long start = System.currentTimeMillis();
- final int times = 200000;
- for (int i = 0; i < times; i++) {
- pSeekable.mark();
- FileUtil.read(pSeekable);
- pSeekable.reset();
- }
- long time = System.currentTimeMillis() - start;
-
- System.out.println("Time; " + time + "ms (" + (time / (float) times) + "ms/inv)");
- }
- */
-
- @Test
- public void testReadResetReadDirectBufferBug() throws IOException {
- // Make sure we use the exact size of the buffer
- final int size = 1024;
-
- // Fill bytes
- byte[] bytes = new byte[size * 2];
- sRandom.nextBytes(bytes);
-
- // Create wrapper stream
- SeekableInputStream stream = makeInputStream(bytes);
-
- // Read to fill the buffer, then reset
- int val;
-
- val = stream.read();
- assertFalse("Unexepected EOF", val == -1);
- val = stream.read();
- assertFalse("Unexepected EOF", val == -1);
- val = stream.read();
- assertFalse("Unexepected EOF", val == -1);
- val = stream.read();
- assertFalse("Unexepected EOF", val == -1);
-
- stream.seek(0);
-
- // Read fully and compare
- byte[] result = new byte[size];
-
- readFully(stream, result);
- assertTrue(rangeEquals(bytes, 0, result, 0, size));
-
- readFully(stream, result);
- assertTrue(rangeEquals(bytes, size, result, 0, size));
- }
-
- @Test
- public void testReadAllByteValuesRegression() throws IOException {
- final int size = 128;
-
- // Fill bytes
- byte[] bytes = new byte[256];
- for (int i = 0; i < bytes.length; i++) {
- bytes[i] = (byte) i;
- }
-
- // Create wrapper stream
- SeekableInputStream stream = makeInputStream(bytes);
-
- // Fill buffer
- byte[] buffer = new byte[size];
- while (stream.read(buffer) >= 0) {
- }
-
- stream.seek(0);
- for (int i = 0; i < bytes.length; i += 2) {
- assertEquals("Wrong stream position", i, stream.getStreamPosition());
- int count = stream.read(buffer, 0, 2);
- assertEquals(2, count);
- assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i], buffer[0]);
- assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i + 1], buffer[1]);
- }
-
- stream.seek(0);
- for (int i = 0; i < bytes.length; i++) {
- assertEquals("Wrong stream position", i, stream.getStreamPosition());
- int actual = stream.read();
- assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i] & 0xff, actual);
- assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i], (byte) actual);
- }
-
- }
-
- @Test
- public void testCloseUnderlyingStream() throws IOException {
- final boolean[] closed = new boolean[1];
-
- ByteArrayInputStream input = new ByteArrayInputStream(makeRandomArray(256)) {
- @Override
- public void close() throws IOException {
- closed[0] = true;
- super.close();
- }
- };
-
- SeekableInputStream stream = makeInputStream(input);
-
- try {
- FileUtil.read(stream); // Read until EOF
-
- assertEquals("EOF not reached (test case broken)", -1, stream.read());
- assertFalse("Underlying stream closed before close", closed[0]);
- }
- finally {
- stream.close();
- }
-
- assertTrue("Underlying stream not closed", closed[0]);
-
- }
-
- private void readFully(InputStream pStream, byte[] pResult) throws IOException {
- int pos = 0;
- while (pos < pResult.length) {
- int read = pStream.read(pResult, pos, pResult.length - pos);
- if (read == -1) {
- throw new EOFException();
- }
- pos += read;
- }
- }
-
- /**
- * Test two arrays for range equality. That is, they contain the same elements for some specified range.
- *
- * @param pFirst one array to test for equality
- * @param pFirstOffset the offset into the first array to start testing for equality
- * @param pSecond the other array to test for equality
- * @param pSecondOffset the offset into the second array to start testing for equality
- * @param pLength the length of the range to check for equality
- *
- * @return {@code true} if both arrays are non-{@code null}
- * and have at least {@code offset + pLength} elements
- * and all elements in the range from the first array is equal to the elements from the second array,
- * or if {@code pFirst == pSecond} (including both arrays being {@code null})
- * and {@code pFirstOffset == pSecondOffset}.
- * Otherwise {@code false}.
- */
- static boolean rangeEquals(byte[] pFirst, int pFirstOffset, byte[] pSecond, int pSecondOffset, int pLength) {
- if (pFirst == pSecond && pFirstOffset == pSecondOffset) {
- return true;
- }
-
- if (pFirst == null || pSecond == null) {
- return false;
- }
-
- if (pFirst.length < pFirstOffset + pLength || pSecond.length < pSecondOffset + pLength) {
- return false;
- }
-
- for (int i = 0; i < pLength; i++) {
- if (pFirst[pFirstOffset + i] != pSecond[pSecondOffset + i]) {
- return false;
- }
- }
-
- return true;
- }
-}
+/*
+ * 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 org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import static org.junit.Assert.*;
+
+/**
+ * SeekableInputStreamAbstractTest
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/SeekableInputStreamAbstractTestCase.java#4 $
+ */
+public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstractTest implements SeekableInterfaceTest {
+ //// TODO: Figure out a better way of creating interface tests without duplicating code
+ final SeekableAbstractTest seekableTestCase = new SeekableAbstractTest() {
+ protected Seekable createSeekable() {
+ return makeInputStream();
+ }
+ };
+
+ @Override
+ protected SeekableInputStream makeInputStream() {
+ return (SeekableInputStream) super.makeInputStream();
+ }
+
+ @Override
+ protected SeekableInputStream makeInputStream(final int pSize) {
+ return (SeekableInputStream) super.makeInputStream(pSize);
+ }
+
+ protected SeekableInputStream makeInputStream(byte[] pBytes) {
+ return makeInputStream(new ByteArrayInputStream(pBytes));
+ }
+
+ protected abstract SeekableInputStream makeInputStream(InputStream pStream);
+
+ @Test
+ @Override
+ public void testResetAfterReset() throws Exception {
+ InputStream input = makeInputStream(makeOrderedArray(25));
+
+ if (!input.markSupported()) {
+ return; // Not supported, skip test
+ }
+
+ assertTrue("Expected to read positive value", input.read() >= 0);
+
+ int readlimit = 5;
+
+ // Mark
+ input.mark(readlimit);
+ int read = input.read();
+ assertTrue("Expected to read positive value", read >= 0);
+
+ input.reset();
+ assertEquals("Expected value read differs from actual", read, input.read());
+
+ // Reset after read limit passed, may either throw exception, or reset to last good mark
+ try {
+ input.reset();
+ assertEquals("Re-read of reset data should be first", 0, input.read());
+ }
+ catch (Exception e) {
+ assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
+ }
+ }
+
+ @Test
+ public void testSeekable() {
+ seekableTestCase.testSeekable();
+ }
+
+ @Test
+ public void testFlushBeyondCurrentPos() throws Exception {
+ SeekableInputStream seekable = makeInputStream(20);
+
+ int pos = 10;
+ try {
+ seekable.flushBefore(pos);
+ fail("Flush beyond current position should throw IndexOutOfBoundsException");
+ }
+ catch (IndexOutOfBoundsException e) {
+ // Ignore
+ }
+ }
+
+ @Test
+ public void testSeek() throws Exception {
+ SeekableInputStream seekable = makeInputStream(55);
+ int pos = 37;
+
+ seekable.seek(pos);
+ long streamPos = seekable.getStreamPosition();
+ assertEquals("Stream positon should match seeked position", pos, streamPos);
+ }
+
+ @Test
+ public void testSeekFlush() throws Exception {
+ SeekableInputStream seekable = makeInputStream(133);
+ int pos = 45;
+ seekable.seek(pos);
+ seekable.flushBefore(pos);
+ long flushedPos = seekable.getFlushedPosition();
+ assertEquals("Flushed positon should match position", pos, flushedPos);
+
+ try {
+ seekable.seek(pos - 1);
+ fail("Read before flushed position succeeded");
+ }
+ catch (IndexOutOfBoundsException e) {
+ // Ignore
+ }
+ }
+
+ @Test
+ public void testMarkFlushReset() throws Exception {
+ SeekableInputStream seekable = makeInputStream(77);
+
+ seekable.mark();
+
+ int position = 55;
+ seekable.seek(position);
+ seekable.flushBefore(position);
+
+ try {
+ seekable.reset();
+ fail("Reset before flushed position succeeded");
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+
+ assertEquals(position, seekable.getStreamPosition());
+ }
+
+ @Test
+ public void testSeekSkipRead() throws Exception {
+ SeekableInputStream seekable = makeInputStream(133);
+ int pos = 45;
+ for (int i = 0; i < 10; i++) {
+ seekable.seek(pos);
+ //noinspection ResultOfMethodCallIgnored
+ seekable.skip(i);
+ byte[] bytes = FileUtil.read(seekable);
+ assertEquals(133, seekable.getStreamPosition());
+ assertEquals(133 - 45- i, bytes.length);
+ }
+ }
+
+ protected void testSeekSkip(SeekableInputStream pSeekable, String pStr) throws IOException {
+ System.out.println();
+ pSeekable.seek(pStr.length());
+ FileUtil.read(pSeekable);
+ for (int i = 0; i < 10; i++) {
+ byte[] bytes = FileUtil.read(pSeekable);
+ int len = bytes.length;
+ if (len != 0) {
+ System.err.println("Error in buffer length after full read...");
+ System.err.println("len: " + len);
+ System.err.println("bytes: \"" + new String(bytes) + "\"");
+ break;
+ }
+ }
+
+ System.out.println();
+
+ for (int i = 0; i < 10; i++) {
+ pSeekable.seek(0);
+ int skip = i * 3;
+ //noinspection ResultOfMethodCallIgnored
+ pSeekable.skip(skip);
+ String str = new String(FileUtil.read(pSeekable));
+ System.out.println(str);
+ if (str.length() != pStr.length() - skip) {
+ throw new Error("Error in buffer length after skip");
+ }
+ }
+
+ System.out.println();
+ System.out.println("seek/skip ok!");
+ System.out.println();
+ }
+
+ protected static void markReset(SeekableInputStream pSeekable) throws IOException {
+ for (int i = 0; i < 10; i++) {
+ pSeekable.mark();
+ System.out.println(new String(FileUtil.read(pSeekable)));
+ pSeekable.reset();
+ }
+
+ System.out.println();
+ System.out.println("mark/reset ok!");
+ }
+
+ protected static void timeRead(SeekableInputStream pSeekable) throws IOException {
+ for (int i = 0; i < 5000; i++) {
+ pSeekable.mark();
+ FileUtil.read(pSeekable);
+ pSeekable.reset();
+ }
+
+ long start = System.currentTimeMillis();
+ final int times = 200000;
+ for (int i = 0; i < times; i++) {
+ pSeekable.mark();
+ FileUtil.read(pSeekable);
+ pSeekable.reset();
+ }
+ long time = System.currentTimeMillis() - start;
+
+ System.out.println("Time; " + time + "ms (" + (time / (float) times) + "ms/inv)");
+ }
+
+ /*
+
+ // Test code below...
+ protected final static String STR = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Fusce massa orci, adipiscing vel, dapibus et, vulputate tristique, tortor. Quisque sodales. Mauris varius turpis et pede. Nam ac dolor vel diam condimentum elementum. Pellentesque eget tellus. Praesent magna. Sed fringilla. Proin ullamcorper tincidunt ante. Fusce dapibus nibh nec dolor. Etiam erat. Nullam dignissim laoreet nibh. Maecenas scelerisque. Pellentesque in quam. Maecenas sollicitudin, magna nec imperdiet facilisis, metus quam tristique ipsum, vitae consequat massa purus eget leo. Nulla ipsum. Proin non purus eget tellus lobortis iaculis. In lorem justo, posuere id, vulputate at, adipiscing ut, nisl. Nunc dui erat, tincidunt ac, interdum quis, rutrum et, libero. Etiam lectus dui, viverra sit amet, elementum ut, malesuada sed, massa. Vestibulum mi nulla, sodales vel, vestibulum sed, congue blandit, velit.";
+
+ protected static void flushSeek(SeekableInputStream pSeekable, String pStr) throws IOException {
+ pSeekable.seek(0);
+ pSeekable.mark();
+ int pos = pStr.length() / 2;
+ try {
+ pSeekable.flushBefore(pos);
+ System.err.println("Error in flush/seek");
+ }
+ catch (IndexOutOfBoundsException e) {
+ // Ignore
+ }
+ pSeekable.seek(pos);
+ long streamPos = pSeekable.getStreamPosition();
+ if (streamPos != pos) {
+ System.err.println("Streampos not equal seeked pos");
+ }
+
+ pSeekable.flushBefore(pos);
+ long flushedPos = pSeekable.getFlushedPosition();
+ if (flushedPos != pos) {
+ System.err.println("flushedpos not equal set flushed pos");
+ }
+
+ for (int i = 0; i < 10; i++) {
+ pSeekable.seek(pos);
+ //noinspection ResultOfMethodCallIgnored
+ pSeekable.skip(i);
+ System.out.println(new String(FileUtil.read(pSeekable)));
+ }
+
+ try {
+ pSeekable.seek(pos - 1);
+ System.err.println("Error in flush/seek");
+ }
+ catch (IndexOutOfBoundsException e) {
+ // Ignore
+ }
+ try {
+ pSeekable.reset();
+ System.err.println("Error in flush/seek");
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+
+ System.out.println();
+ System.out.println("flush/seek ok!");
+ }
+
+ protected static void seekSkip(SeekableInputStream pSeekable, String pStr) throws IOException {
+ System.out.println();
+ pSeekable.seek(pStr.length());
+ FileUtil.read(pSeekable);
+ for (int i = 0; i < 10; i++) {
+ byte[] bytes = FileUtil.read(pSeekable);
+ int len = bytes.length;
+ if (len != 0) {
+ System.err.println("Error in buffer length after full read...");
+ System.err.println("len: " + len);
+ System.err.println("bytes: \"" + new String(bytes) + "\"");
+ break;
+ }
+ }
+
+ System.out.println();
+
+ for (int i = 0; i < 10; i++) {
+ pSeekable.seek(0);
+ int skip = i * 3;
+ //noinspection ResultOfMethodCallIgnored
+ pSeekable.skip(skip);
+ String str = new String(FileUtil.read(pSeekable));
+ System.out.println(str);
+ if (str.length() != pStr.length() - skip) {
+ throw new Error("Error in buffer length after skip");
+ }
+ }
+
+ System.out.println();
+ System.out.println("seek/skip ok!");
+ System.out.println();
+ }
+
+ protected static void markReset(SeekableInputStream pSeekable) throws IOException {
+ for (int i = 0; i < 10; i++) {
+ pSeekable.mark();
+ System.out.println(new String(FileUtil.read(pSeekable)));
+ pSeekable.reset();
+ }
+
+ System.out.println();
+ System.out.println("mark/reset ok!");
+ }
+
+ protected static void timeRead(SeekableInputStream pSeekable) throws IOException {
+ for (int i = 0; i < 5000; i++) {
+ pSeekable.mark();
+ FileUtil.read(pSeekable);
+ pSeekable.reset();
+ }
+
+ long start = System.currentTimeMillis();
+ final int times = 200000;
+ for (int i = 0; i < times; i++) {
+ pSeekable.mark();
+ FileUtil.read(pSeekable);
+ pSeekable.reset();
+ }
+ long time = System.currentTimeMillis() - start;
+
+ System.out.println("Time; " + time + "ms (" + (time / (float) times) + "ms/inv)");
+ }
+ */
+
+ @Test
+ public void testReadResetReadDirectBufferBug() throws IOException {
+ // Make sure we use the exact size of the buffer
+ final int size = 1024;
+
+ // Fill bytes
+ byte[] bytes = new byte[size * 2];
+ sRandom.nextBytes(bytes);
+
+ // Create wrapper stream
+ SeekableInputStream stream = makeInputStream(bytes);
+
+ // Read to fill the buffer, then reset
+ int val;
+
+ val = stream.read();
+ assertFalse("Unexepected EOF", val == -1);
+ val = stream.read();
+ assertFalse("Unexepected EOF", val == -1);
+ val = stream.read();
+ assertFalse("Unexepected EOF", val == -1);
+ val = stream.read();
+ assertFalse("Unexepected EOF", val == -1);
+
+ stream.seek(0);
+
+ // Read fully and compare
+ byte[] result = new byte[size];
+
+ readFully(stream, result);
+ assertTrue(rangeEquals(bytes, 0, result, 0, size));
+
+ readFully(stream, result);
+ assertTrue(rangeEquals(bytes, size, result, 0, size));
+ }
+
+ @Test
+ public void testReadAllByteValuesRegression() throws IOException {
+ final int size = 128;
+
+ // Fill bytes
+ byte[] bytes = new byte[256];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = (byte) i;
+ }
+
+ // Create wrapper stream
+ SeekableInputStream stream = makeInputStream(bytes);
+
+ // Fill buffer
+ byte[] buffer = new byte[size];
+ while (stream.read(buffer) >= 0) {
+ }
+
+ stream.seek(0);
+ for (int i = 0; i < bytes.length; i += 2) {
+ assertEquals("Wrong stream position", i, stream.getStreamPosition());
+ int count = stream.read(buffer, 0, 2);
+ assertEquals(2, count);
+ assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i], buffer[0]);
+ assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i + 1], buffer[1]);
+ }
+
+ stream.seek(0);
+ for (int i = 0; i < bytes.length; i++) {
+ assertEquals("Wrong stream position", i, stream.getStreamPosition());
+ int actual = stream.read();
+ assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i] & 0xff, actual);
+ assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i], (byte) actual);
+ }
+
+ }
+
+ @Test
+ public void testCloseUnderlyingStream() throws IOException {
+ final boolean[] closed = new boolean[1];
+
+ ByteArrayInputStream input = new ByteArrayInputStream(makeRandomArray(256)) {
+ @Override
+ public void close() throws IOException {
+ closed[0] = true;
+ super.close();
+ }
+ };
+
+ SeekableInputStream stream = makeInputStream(input);
+
+ try {
+ FileUtil.read(stream); // Read until EOF
+
+ assertEquals("EOF not reached (test case broken)", -1, stream.read());
+ assertFalse("Underlying stream closed before close", closed[0]);
+ }
+ finally {
+ stream.close();
+ }
+
+ assertTrue("Underlying stream not closed", closed[0]);
+
+ }
+
+ private void readFully(InputStream pStream, byte[] pResult) throws IOException {
+ int pos = 0;
+ while (pos < pResult.length) {
+ int read = pStream.read(pResult, pos, pResult.length - pos);
+ if (read == -1) {
+ throw new EOFException();
+ }
+ pos += read;
+ }
+ }
+
+ /**
+ * Test two arrays for range equality. That is, they contain the same elements for some specified range.
+ *
+ * @param pFirst one array to test for equality
+ * @param pFirstOffset the offset into the first array to start testing for equality
+ * @param pSecond the other array to test for equality
+ * @param pSecondOffset the offset into the second array to start testing for equality
+ * @param pLength the length of the range to check for equality
+ *
+ * @return {@code true} if both arrays are non-{@code null}
+ * and have at least {@code offset + pLength} elements
+ * and all elements in the range from the first array is equal to the elements from the second array,
+ * or if {@code pFirst == pSecond} (including both arrays being {@code null})
+ * and {@code pFirstOffset == pSecondOffset}.
+ * Otherwise {@code false}.
+ */
+ static boolean rangeEquals(byte[] pFirst, int pFirstOffset, byte[] pSecond, int pSecondOffset, int pLength) {
+ if (pFirst == pSecond && pFirstOffset == pSecondOffset) {
+ return true;
+ }
+
+ if (pFirst == null || pSecond == null) {
+ return false;
+ }
+
+ if (pFirst.length < pFirstOffset + pLength || pSecond.length < pSecondOffset + pLength) {
+ return false;
+ }
+
+ for (int i = 0; i < pLength; i++) {
+ if (pFirst[pFirstOffset + i] != pSecond[pSecondOffset + i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableInterfaceTest.java b/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableInterfaceTest.java
index 1c59b52c..2a5e92bc 100755
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableInterfaceTest.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/SeekableInterfaceTest.java
@@ -1,3 +1,33 @@
+/*
+ * 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;
/**
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/StringArrayReaderTest.java b/common/common-io/src/test/java/com/twelvemonkeys/io/StringArrayReaderTest.java
new file mode 100755
index 00000000..53af2336
--- /dev/null
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/StringArrayReaderTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.StringUtil;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import static org.junit.Assert.*;
+
+/**
+ * StringArrayReaderTestCase
+ *
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/StringArrayReaderTestCase.java#1 $
+ */
+public class StringArrayReaderTest extends ReaderAbstractTest {
+
+ protected Reader makeReader(String pInput) {
+ // Split
+ String[] input = StringUtil.toStringArray(pInput, " ");
+ // Reappend spaces...
+ for (int i = 0; i < input.length; i++) {
+ if (i != 0) {
+ input[i] = " " + input[i];
+ }
+ }
+
+ return new StringArrayReader(input);
+ }
+
+ @Test
+ public void testNullConstructor() {
+ try {
+ new StringArrayReader(null);
+ fail("Should not allow null argument");
+ }
+ catch (RuntimeException e) {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testEmptyArrayConstructor() throws IOException {
+ Reader reader = new StringArrayReader(new String[0]);
+ assertEquals(-1, reader.read());
+ }
+
+ @Test
+ public void testEmptyStringConstructor() throws IOException {
+ Reader reader = new StringArrayReader(new String[] {""});
+ assertEquals(-1, reader.read());
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/StringArrayReaderTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/StringArrayReaderTestCase.java
deleted file mode 100755
index cf1594c9..00000000
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/StringArrayReaderTestCase.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.twelvemonkeys.io;
-
-import com.twelvemonkeys.lang.StringUtil;
-import org.junit.Test;
-
-import java.io.Reader;
-import java.io.IOException;
-
-import static org.junit.Assert.*;
-
-/**
- * StringArrayReaderTestCase
- *
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/StringArrayReaderTestCase.java#1 $
- */
-public class StringArrayReaderTestCase extends ReaderAbstractTestCase {
-
- protected Reader makeReader(String pInput) {
- // Split
- String[] input = StringUtil.toStringArray(pInput, " ");
- // Reappend spaces...
- for (int i = 0; i < input.length; i++) {
- if (i != 0) {
- input[i] = " " + input[i];
- }
- }
-
- return new StringArrayReader(input);
- }
-
- @Test
- public void testNullConstructor() {
- try {
- new StringArrayReader(null);
- fail("Should not allow null argument");
- }
- catch (RuntimeException e) {
- assertNotNull(e.getMessage());
- }
- }
-
- @Test
- public void testEmptyArrayConstructor() throws IOException {
- Reader reader = new StringArrayReader(new String[0]);
- assertEquals(-1, reader.read());
- }
-
- @Test
- public void testEmptyStringConstructor() throws IOException {
- Reader reader = new StringArrayReader(new String[] {""});
- assertEquals(-1, reader.read());
- }
-}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/Base64DecoderTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/Base64DecoderTest.java
similarity index 56%
rename from common/common-io/src/test/java/com/twelvemonkeys/io/enc/Base64DecoderTestCase.java
rename to common/common-io/src/test/java/com/twelvemonkeys/io/enc/Base64DecoderTest.java
index 922bf5b5..457fb901 100755
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/Base64DecoderTestCase.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/Base64DecoderTest.java
@@ -1,71 +1,103 @@
-package com.twelvemonkeys.io.enc;
-
-
-import com.twelvemonkeys.io.FileUtil;
-import org.junit.Test;
-
-import java.io.*;
-
-import static org.junit.Assert.*;
-
-/**
- * Base64DecoderTest
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/Base64DecoderTestCase.java#1 $
- */
-public class Base64DecoderTestCase extends DecoderAbstractTestCase {
-
- public Decoder createDecoder() {
- return new Base64Decoder();
- }
-
- public Encoder createCompatibleEncoder() {
- return new Base64Encoder();
- }
-
- @Test
- public void testEmptyDecode2() throws IOException {
- String data = "";
-
- InputStream in = new DecoderStream(new ByteArrayInputStream(data.getBytes()), createDecoder());
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-
- FileUtil.copy(in, bytes);
-
- assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
- }
-
- @Test
- public void testShortDecode() throws IOException {
- String data = "dGVzdA==";
-
- InputStream in = new DecoderStream(new ByteArrayInputStream(data.getBytes()), createDecoder());
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-
- FileUtil.copy(in, bytes);
-
- assertEquals("Strings does not match", "test", new String(bytes.toByteArray()));
- }
-
- @Test
- public void testLongDecode() throws IOException {
- String data = "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIEZ1" +
- "c2NlIGVzdC4gTW9yYmkgbHVjdHVzIGNvbnNlY3RldHVlciBqdXN0by4gVml2YW11cyBkYXBpYnVzIGxh" +
- "b3JlZXQgcHVydXMuIE51bmMgdml2ZXJyYSBkaWN0dW0gbmlzbC4gSW50ZWdlciB1bGxhbWNvcnBlciwg" +
- "bmlzaSBpbiBkaWN0dW0gYW1ldC4=";
-
- InputStream in = new DecoderStream(new ByteArrayInputStream(data.getBytes()), createDecoder());
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-
- FileUtil.copy(in, bytes);
-
- assertEquals("Strings does not match",
- "Lorem ipsum dolor sit amet, consectetuer adipiscing " +
- "elit. Fusce est. Morbi luctus consectetuer justo. Vivamus " +
- "dapibus laoreet purus. Nunc viverra dictum nisl. Integer " +
- "ullamcorper, nisi in dictum amet.",
- new String(bytes.toByteArray()));
- }
+/*
+ * 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 com.twelvemonkeys.io.FileUtil;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Base64DecoderTest
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/Base64DecoderTestCase.java#1 $
+ */
+public class Base64DecoderTest extends DecoderAbstractTest {
+
+ public Decoder createDecoder() {
+ return new Base64Decoder();
+ }
+
+ public Encoder createCompatibleEncoder() {
+ return new Base64Encoder();
+ }
+
+ @Test
+ public void testEmptyDecode2() throws IOException {
+ String data = "";
+
+ InputStream in = new DecoderStream(new ByteArrayInputStream(data.getBytes()), createDecoder());
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+
+ FileUtil.copy(in, bytes);
+
+ assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
+ }
+
+ @Test
+ public void testShortDecode() throws IOException {
+ String data = "dGVzdA==";
+
+ InputStream in = new DecoderStream(new ByteArrayInputStream(data.getBytes()), createDecoder());
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+
+ FileUtil.copy(in, bytes);
+
+ assertEquals("Strings does not match", "test", new String(bytes.toByteArray()));
+ }
+
+ @Test
+ public void testLongDecode() throws IOException {
+ String data = "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIEZ1" +
+ "c2NlIGVzdC4gTW9yYmkgbHVjdHVzIGNvbnNlY3RldHVlciBqdXN0by4gVml2YW11cyBkYXBpYnVzIGxh" +
+ "b3JlZXQgcHVydXMuIE51bmMgdml2ZXJyYSBkaWN0dW0gbmlzbC4gSW50ZWdlciB1bGxhbWNvcnBlciwg" +
+ "bmlzaSBpbiBkaWN0dW0gYW1ldC4=";
+
+ InputStream in = new DecoderStream(new ByteArrayInputStream(data.getBytes()), createDecoder());
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+
+ FileUtil.copy(in, bytes);
+
+ assertEquals("Strings does not match",
+ "Lorem ipsum dolor sit amet, consectetuer adipiscing " +
+ "elit. Fusce est. Morbi luctus consectetuer justo. Vivamus " +
+ "dapibus laoreet purus. Nunc viverra dictum nisl. Integer " +
+ "ullamcorper, nisi in dictum amet.",
+ new String(bytes.toByteArray()));
+ }
}
\ No newline at end of file
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/Base64EncoderTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/Base64EncoderTest.java
similarity index 58%
rename from common/common-io/src/test/java/com/twelvemonkeys/io/enc/Base64EncoderTestCase.java
rename to common/common-io/src/test/java/com/twelvemonkeys/io/enc/Base64EncoderTest.java
index 0c3e7720..7e77196a 100644
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/Base64EncoderTestCase.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/Base64EncoderTest.java
@@ -1,68 +1,98 @@
-package com.twelvemonkeys.io.enc;
-
-import org.junit.Test;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import static org.junit.Assert.*;
-
-/**
- * Base64EncoderTest
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/Base64EncoderTestCase.java#1 $
- */
-public class Base64EncoderTestCase extends EncoderAbstractTestCase {
-
- protected Encoder createEncoder() {
- return new Base64Encoder();
- }
-
- protected Decoder createCompatibleDecoder() {
- return new Base64Decoder();
- }
-
- @Test
- public void testEmptyEncode() throws IOException {
- String data = "";
-
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- OutputStream out = new EncoderStream(bytes, createEncoder(), true);
- out.write(data.getBytes());
-
- assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
- }
-
- @Test
- public void testShortEncode() throws IOException {
- String data = "test";
-
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- OutputStream out = new EncoderStream(bytes, createEncoder(), true);
- out.write(data.getBytes());
-
- assertEquals("Strings does not match", "dGVzdA==", new String(bytes.toByteArray()));
- }
-
- @Test
- public void testLongEncode() throws IOException {
- String data = "Lorem ipsum dolor sit amet, consectetuer adipiscing " +
- "elit. Fusce est. Morbi luctus consectetuer justo. Vivamus " +
- "dapibus laoreet purus. Nunc viverra dictum nisl. Integer " +
- "ullamcorper, nisi in dictum amet.";
-
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- OutputStream out = new EncoderStream(bytes, createEncoder(), true);
- out.write(data.getBytes());
-
- assertEquals("Strings does not match",
- "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIEZ1" +
- "c2NlIGVzdC4gTW9yYmkgbHVjdHVzIGNvbnNlY3RldHVlciBqdXN0by4gVml2YW11cyBkYXBpYnVzIGxh" +
- "b3JlZXQgcHVydXMuIE51bmMgdml2ZXJyYSBkaWN0dW0gbmlzbC4gSW50ZWdlciB1bGxhbWNvcnBlciwg" +
- "bmlzaSBpbiBkaWN0dW0gYW1ldC4=",
- new String(bytes.toByteArray()));
- }
-}
+/*
+ * 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 org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Base64EncoderTest
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/Base64EncoderTestCase.java#1 $
+ */
+public class Base64EncoderTest extends EncoderAbstractTest {
+
+ protected Encoder createEncoder() {
+ return new Base64Encoder();
+ }
+
+ protected Decoder createCompatibleDecoder() {
+ return new Base64Decoder();
+ }
+
+ @Test
+ public void testEmptyEncode() throws IOException {
+ String data = "";
+
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream out = new EncoderStream(bytes, createEncoder(), true);
+ out.write(data.getBytes());
+
+ assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
+ }
+
+ @Test
+ public void testShortEncode() throws IOException {
+ String data = "test";
+
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream out = new EncoderStream(bytes, createEncoder(), true);
+ out.write(data.getBytes());
+
+ assertEquals("Strings does not match", "dGVzdA==", new String(bytes.toByteArray()));
+ }
+
+ @Test
+ public void testLongEncode() throws IOException {
+ String data = "Lorem ipsum dolor sit amet, consectetuer adipiscing " +
+ "elit. Fusce est. Morbi luctus consectetuer justo. Vivamus " +
+ "dapibus laoreet purus. Nunc viverra dictum nisl. Integer " +
+ "ullamcorper, nisi in dictum amet.";
+
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream out = new EncoderStream(bytes, createEncoder(), true);
+ out.write(data.getBytes());
+
+ assertEquals("Strings does not match",
+ "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIEZ1" +
+ "c2NlIGVzdC4gTW9yYmkgbHVjdHVzIGNvbnNlY3RldHVlciBqdXN0by4gVml2YW11cyBkYXBpYnVzIGxh" +
+ "b3JlZXQgcHVydXMuIE51bmMgdml2ZXJyYSBkaWN0dW0gbmlzbC4gSW50ZWdlciB1bGxhbWNvcnBlciwg" +
+ "bmlzaSBpbiBkaWN0dW0gYW1ldC4=",
+ new String(bytes.toByteArray()));
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/DecoderAbstractTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/DecoderAbstractTest.java
similarity index 65%
rename from common/common-io/src/test/java/com/twelvemonkeys/io/enc/DecoderAbstractTestCase.java
rename to common/common-io/src/test/java/com/twelvemonkeys/io/enc/DecoderAbstractTest.java
index 51788010..6f1e8948 100644
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/DecoderAbstractTestCase.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/DecoderAbstractTest.java
@@ -1,115 +1,145 @@
-package com.twelvemonkeys.io.enc;
-
-import com.twelvemonkeys.io.FileUtil;
-import com.twelvemonkeys.lang.ObjectAbstractTestCase;
-import org.junit.Test;
-
-import java.io.*;
-import java.nio.ByteBuffer;
-
-import static org.junit.Assert.*;
-
-/**
- * AbstractDecoderTest
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/DecoderAbstractTestCase.java#1 $
- */
-public abstract class DecoderAbstractTestCase extends ObjectAbstractTestCase {
-
- public abstract Decoder createDecoder();
- public abstract Encoder createCompatibleEncoder();
-
- protected Object makeObject() {
- return createDecoder();
- }
-
- @Test(expected = NullPointerException.class)
- public final void testNullDecode() throws IOException {
- Decoder decoder = createDecoder();
- ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[20]);
-
- decoder.decode(bytes, null);
- fail("null should throw NullPointerException");
- }
-
- @Test
- public final void testEmptyDecode() throws IOException {
- Decoder decoder = createDecoder();
- ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[0]);
-
- try {
- int count = decoder.decode(bytes, ByteBuffer.allocate(128));
- assertEquals("Should not be able to read any bytes", 0, count);
- }
- catch (EOFException allowed) {
- // Okay
- }
- }
-
- private byte[] createData(int pLength) throws Exception {
- byte[] bytes = new byte[pLength];
- EncoderAbstractTestCase.RANDOM.nextBytes(bytes);
- return bytes;
- }
-
- private void runStreamTest(int pLength) throws Exception {
- byte[] data = createData(pLength);
-
- ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
- OutputStream out = new EncoderStream(outBytes, createCompatibleEncoder(), true);
- out.write(data);
- out.close();
- byte[] encoded = outBytes.toByteArray();
-
- byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createDecoder()));
- assertArrayEquals(String.format("Data %d", pLength), data, decoded);
-
- InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createDecoder());
- outBytes = new ByteArrayOutputStream();
- FileUtil.copy(in, outBytes);
- outBytes.close();
- in.close();
-
- decoded = outBytes.toByteArray();
- assertArrayEquals(String.format("Data %d", pLength), data, decoded);
- }
-
- @Test
- public final void testStreams() throws Exception {
- if (createCompatibleEncoder() == null) {
- return;
- }
-
- for (int i = 1; i < 100; i++) {
- try {
- runStreamTest(i);
- }
- catch (IOException e) {
- e.printStackTrace();
- fail(e.getMessage() + ": " + i);
- }
- }
-
- for (int i = 100; i < 2000; i += 250) {
- try {
- runStreamTest(i);
- }
- catch (IOException e) {
- e.printStackTrace();
- fail(e.getMessage() + ": " + i);
- }
- }
-
- for (int i = 2000; i < 80000; i += 1000) {
- try {
- runStreamTest(i);
- }
- catch (IOException e) {
- e.printStackTrace();
- fail(e.getMessage() + ": " + i);
- }
- }
- }
-}
+/*
+ * 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 com.twelvemonkeys.io.FileUtil;
+import com.twelvemonkeys.lang.ObjectAbstractTest;
+import org.junit.Test;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+
+import static org.junit.Assert.*;
+
+/**
+ * AbstractDecoderTest
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/DecoderAbstractTestCase.java#1 $
+ */
+public abstract class DecoderAbstractTest extends ObjectAbstractTest {
+
+ public abstract Decoder createDecoder();
+ public abstract Encoder createCompatibleEncoder();
+
+ protected Object makeObject() {
+ return createDecoder();
+ }
+
+ @Test(expected = NullPointerException.class)
+ public final void testNullDecode() throws IOException {
+ Decoder decoder = createDecoder();
+ ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[20]);
+
+ decoder.decode(bytes, null);
+ fail("null should throw NullPointerException");
+ }
+
+ @Test
+ public final void testEmptyDecode() throws IOException {
+ Decoder decoder = createDecoder();
+ ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[0]);
+
+ try {
+ int count = decoder.decode(bytes, ByteBuffer.allocate(128));
+ assertEquals("Should not be able to read any bytes", 0, count);
+ }
+ catch (EOFException allowed) {
+ // Okay
+ }
+ }
+
+ private byte[] createData(int pLength) throws Exception {
+ byte[] bytes = new byte[pLength];
+ EncoderAbstractTest.RANDOM.nextBytes(bytes);
+ return bytes;
+ }
+
+ private void runStreamTest(int pLength) throws Exception {
+ byte[] data = createData(pLength);
+
+ ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
+ OutputStream out = new EncoderStream(outBytes, createCompatibleEncoder(), true);
+ out.write(data);
+ out.close();
+ byte[] encoded = outBytes.toByteArray();
+
+ byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createDecoder()));
+ assertArrayEquals(String.format("Data %d", pLength), data, decoded);
+
+ InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createDecoder());
+ outBytes = new ByteArrayOutputStream();
+ FileUtil.copy(in, outBytes);
+ outBytes.close();
+ in.close();
+
+ decoded = outBytes.toByteArray();
+ assertArrayEquals(String.format("Data %d", pLength), data, decoded);
+ }
+
+ @Test
+ public final void testStreams() throws Exception {
+ if (createCompatibleEncoder() == null) {
+ return;
+ }
+
+ for (int i = 1; i < 100; i++) {
+ try {
+ runStreamTest(i);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ fail(e.getMessage() + ": " + i);
+ }
+ }
+
+ for (int i = 100; i < 2000; i += 250) {
+ try {
+ runStreamTest(i);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ fail(e.getMessage() + ": " + i);
+ }
+ }
+
+ for (int i = 2000; i < 80000; i += 1000) {
+ try {
+ runStreamTest(i);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ fail(e.getMessage() + ": " + i);
+ }
+ }
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/EncoderAbstractTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/EncoderAbstractTest.java
similarity index 60%
rename from common/common-io/src/test/java/com/twelvemonkeys/io/enc/EncoderAbstractTestCase.java
rename to common/common-io/src/test/java/com/twelvemonkeys/io/enc/EncoderAbstractTest.java
index 6376a73d..863f04b7 100644
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/EncoderAbstractTestCase.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/EncoderAbstractTest.java
@@ -1,139 +1,152 @@
-package com.twelvemonkeys.io.enc;
-
-import com.twelvemonkeys.io.FileUtil;
-import com.twelvemonkeys.lang.ObjectAbstractTestCase;
-import org.junit.Test;
-
-import java.io.*;
-import java.util.Arrays;
-import java.util.Random;
-
-import static org.junit.Assert.*;
-
-/**
- * AbstractEncoderTest
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/EncoderAbstractTestCase.java#1 $
- */
-public abstract class EncoderAbstractTestCase extends ObjectAbstractTestCase {
- // Use seed to make sure we create same number all the time
- static final long SEED = 12345678;
- static final Random RANDOM = new Random(SEED);
-
- protected abstract Encoder createEncoder();
- protected abstract Decoder createCompatibleDecoder();
-
- protected Object makeObject() {
- return createEncoder();
- }
-
- @Test
- public final void testNullEncode() throws IOException {
- Encoder encoder = createEncoder();
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-
- try {
- encoder.encode(bytes, null);
- fail("null should throw NullPointerException");
- }
- catch (NullPointerException expected) {
- }
- }
-
- private byte[] createData(final int pLength) throws Exception {
- byte[] bytes = new byte[pLength];
- RANDOM.nextBytes(bytes);
- return bytes;
- }
-
- private void runStreamTest(final int pLength) throws Exception {
- byte[] data = createData(pLength);
- ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
- OutputStream out = new EncoderStream(outBytes, createEncoder(), true);
-
- try {
- // Provoke failure for encoders that doesn't take array offset properly into account
- int off = (data.length + 1) / 2;
- out.write(data, 0, off);
- if (data.length > off) {
- out.write(data, off, data.length - off);
- }
- }
- finally {
- out.close();
- }
-
- byte[] encoded = outBytes.toByteArray();
-
-// System.err.println("encoded.length: " + encoded.length);
-// System.err.println("encoded: " + Arrays.toString(encoded));
-
- byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()));
- assertTrue(Arrays.equals(data, decoded));
-
- InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
- outBytes = new ByteArrayOutputStream();
-
- try {
- FileUtil.copy(in, outBytes);
- }
- finally {
- outBytes.close();
- in.close();
- }
-
- decoded = outBytes.toByteArray();
- assertTrue(Arrays.equals(data, decoded));
- }
-
- @Test
- public final void testStreams() throws Exception {
- for (int i = 0; i < 100; i++) {
- try {
- runStreamTest(i);
- }
- catch (IOException e) {
- e.printStackTrace();
- fail(e.getMessage() + ": " + i);
- }
- catch (Exception e) {
- e.printStackTrace();
- fail(e.getMessage() + ": " + i);
- }
- }
-
- for (int i = 100; i < 2000; i += 250) {
- try {
- runStreamTest(i);
- }
- catch (IOException e) {
- e.printStackTrace();
- fail(e.getMessage() + ": " + i);
- }
- catch (Exception e) {
- e.printStackTrace();
- fail(e.getMessage() + ": " + i);
- }
- }
-
- for (int i = 2000; i < 80000; i += 1000) {
- try {
- runStreamTest(i);
- }
- catch (IOException e) {
- e.printStackTrace();
- fail(e.getMessage() + ": " + i);
- }
- catch (Exception e) {
- e.printStackTrace();
- fail(e.getMessage() + ": " + i);
- }
- }
- }
-
- // TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with 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 com.twelvemonkeys.io.FileUtil;
+import com.twelvemonkeys.lang.ObjectAbstractTest;
+
+import org.junit.Test;
+
+import java.io.*;
+import java.util.Random;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * AbstractEncoderTest
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/EncoderAbstractTestCase.java#1 $
+ */
+public abstract class EncoderAbstractTest extends ObjectAbstractTest {
+ // Use seed to make sure we create same number all the time
+ static final long SEED = 12345678;
+ static final Random RANDOM = new Random(SEED);
+
+ protected abstract Encoder createEncoder();
+ protected abstract Decoder createCompatibleDecoder();
+
+ protected Object makeObject() {
+ return createEncoder();
+ }
+
+ @Test
+ public final void testNullEncode() throws IOException {
+ Encoder encoder = createEncoder();
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+
+ try {
+ encoder.encode(bytes, null);
+ fail("null should throw NullPointerException");
+ }
+ catch (NullPointerException expected) {
+ }
+ }
+
+ private byte[] createData(final int pLength) {
+ byte[] bytes = new byte[pLength];
+ RANDOM.nextBytes(bytes);
+ return bytes;
+ }
+
+ private void runStreamTest(final int pLength) throws Exception {
+ byte[] data = createData(pLength);
+ ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
+
+ try (OutputStream out = new EncoderStream(outBytes, createEncoder(), true)) {
+ // Provoke failure for encoders that doesn't take array offset properly into account
+ int off = (data.length + 1) / 2;
+ out.write(data, 0, off);
+ if (data.length > off) {
+ out.write(data, off, data.length - off);
+ }
+ }
+
+ byte[] encoded = outBytes.toByteArray();
+
+// System.err.println("encoded.length: " + encoded.length);
+// System.err.println("encoded: " + Arrays.toString(encoded));
+
+ byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()));
+ assertArrayEquals(data, decoded);
+
+ InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
+ outBytes = new ByteArrayOutputStream();
+
+ try {
+ FileUtil.copy(in, outBytes);
+ }
+ finally {
+ outBytes.close();
+ in.close();
+ }
+
+ decoded = outBytes.toByteArray();
+ assertArrayEquals(data, decoded);
+ }
+
+ @Test
+ public final void testStreams() throws Exception {
+ for (int i = 0; i < 100; i++) {
+ try {
+ runStreamTest(i);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ fail(e.getMessage() + ": " + i);
+ }
+ }
+
+ for (int i = 100; i < 2000; i += 250) {
+ try {
+ runStreamTest(i);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ fail(e.getMessage() + ": " + i);
+ }
+ }
+
+ for (int i = 2000; i < 80000; i += 1000) {
+ try {
+ runStreamTest(i);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ fail(e.getMessage() + ": " + i);
+ }
+ }
+ }
+
+ // TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/PackBitsDecoderTest.java b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/PackBitsDecoderTest.java
new file mode 100755
index 00000000..6f974936
--- /dev/null
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/PackBitsDecoderTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+/**
+ * PackBitsDecoderTest
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/PackBitsDecoderTestCase.java#1 $
+ */
+public class PackBitsDecoderTest extends DecoderAbstractTest {
+ public Decoder createDecoder() {
+ return new PackBitsDecoder();
+ }
+
+ public Encoder createCompatibleEncoder() {
+ return new PackBitsEncoder();
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/PackBitsDecoderTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/PackBitsDecoderTestCase.java
deleted file mode 100755
index 584be832..00000000
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/PackBitsDecoderTestCase.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.twelvemonkeys.io.enc;
-
-/**
- * PackBitsDecoderTest
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/PackBitsDecoderTestCase.java#1 $
- */
-public class PackBitsDecoderTestCase extends DecoderAbstractTestCase {
- public Decoder createDecoder() {
- return new PackBitsDecoder();
- }
-
- public Encoder createCompatibleEncoder() {
- return new PackBitsEncoder();
- }
-}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/PackBitsEncoderTest.java b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/PackBitsEncoderTest.java
new file mode 100755
index 00000000..b1a13755
--- /dev/null
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/PackBitsEncoderTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+/**
+ * PackBitsEncoderTest
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/PackBitsEncoderTestCase.java#1 $
+ */
+public class PackBitsEncoderTest extends EncoderAbstractTest {
+ protected Encoder createEncoder() {
+ return new PackBitsEncoder();
+ }
+
+ protected Decoder createCompatibleDecoder() {
+ return new PackBitsDecoder();
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/PackBitsEncoderTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/PackBitsEncoderTestCase.java
deleted file mode 100755
index d214fd3f..00000000
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/PackBitsEncoderTestCase.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.twelvemonkeys.io.enc;
-
-/**
- * PackBitsEncoderTest
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/PackBitsEncoderTestCase.java#1 $
- */
-public class PackBitsEncoderTestCase extends EncoderAbstractTestCase {
- protected Encoder createEncoder() {
- return new PackBitsEncoder();
- }
-
- protected Decoder createCompatibleDecoder() {
- return new PackBitsDecoder();
- }
-}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocumentTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocumentTest.java
similarity index 52%
rename from common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocumentTestCase.java
rename to common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocumentTest.java
index ccbf18da..a9efc698 100755
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocumentTestCase.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocumentTest.java
@@ -1,3 +1,33 @@
+/*
+ * Copyright (c) 2011, 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.MemoryCacheSeekableStream;
@@ -22,7 +52,7 @@ import static org.junit.Assert.*;
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocumentTestCase.java#1 $
*/
-public class CompoundDocumentTestCase {
+public class CompoundDocumentTest {
private static final String SAMPLE_DATA = "/Thumbs-camera.db";
@@ -42,64 +72,64 @@ public class CompoundDocumentTestCase {
@Test
public void testRoot() throws IOException {
- CompoundDocument document = createTestDocument();
+ try (CompoundDocument document = createTestDocument()) {
+ Entry root = document.getRootEntry();
- Entry root = document.getRootEntry();
-
- assertNotNull(root);
- assertEquals("Root Entry", root.getName());
- assertTrue(root.isRoot());
- assertFalse(root.isFile());
- assertFalse(root.isDirectory());
- assertEquals(0, root.length());
- assertNull(root.getInputStream());
+ assertNotNull(root);
+ assertEquals("Root Entry", root.getName());
+ assertTrue(root.isRoot());
+ assertFalse(root.isFile());
+ assertFalse(root.isDirectory());
+ assertEquals(0, root.length());
+ assertNull(root.getInputStream());
+ }
}
@Test
public void testContents() throws IOException {
- CompoundDocument document = createTestDocument();
+ try (CompoundDocument document = createTestDocument()) {
+ Entry root = document.getRootEntry();
- Entry root = document.getRootEntry();
+ assertNotNull(root);
- assertNotNull(root);
+ SortedSet children = new TreeSet(root.getChildEntries());
+ assertEquals(25, children.size());
- SortedSet children = new TreeSet(root.getChildEntries());
- assertEquals(25, children.size());
-
- // Weirdness in the file format, name is *written backwards* 1-24 + Catalog
- for (String name : "1,2,3,4,5,6,7,8,9,01,02,11,12,21,22,31,32,41,42,51,61,71,81,91,Catalog".split(",")) {
- assertEquals(name, children.first().getName());
- children.remove(children.first());
+ // Weirdness in the file format, name is *written backwards* 1-24 + Catalog
+ for (String name : "1,2,3,4,5,6,7,8,9,01,02,11,12,21,22,31,32,41,42,51,61,71,81,91,Catalog".split(",")) {
+ assertEquals(name, children.first().getName());
+ children.remove(children.first());
+ }
}
}
@Test(expected = UnsupportedOperationException.class)
public void testChildEntriesUnmodifiable() throws IOException {
- CompoundDocument document = createTestDocument();
+ try (CompoundDocument document = createTestDocument()) {
+ Entry root = document.getRootEntry();
- Entry root = document.getRootEntry();
+ assertNotNull(root);
- assertNotNull(root);
+ SortedSet children = root.getChildEntries();
- SortedSet children = root.getChildEntries();
-
- // Should not be allowed, as it modifies the internal structure
- children.remove(children.first());
+ // Should not be allowed, as it modifies the internal structure
+ children.remove(children.first());
+ }
}
@Test
public void testReadThumbsCatalogFile() throws IOException {
- CompoundDocument document = createTestDocument();
+ try (CompoundDocument document = createTestDocument()) {
+ Entry root = document.getRootEntry();
- Entry root = document.getRootEntry();
+ assertNotNull(root);
+ assertEquals(25, root.getChildEntries().size());
- assertNotNull(root);
- assertEquals(25, root.getChildEntries().size());
+ Entry catalog = root.getChildEntry("Catalog");
- Entry catalog = root.getChildEntry("Catalog");
-
- assertNotNull(catalog);
- assertNotNull("Input stream may not be null", catalog.getInputStream());
+ assertNotNull(catalog);
+ assertNotNull("Input stream may not be null", catalog.getInputStream());
+ }
}
@Test
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocument_SeekableLittleEndianDataInputStreamTest.java b/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocument_SeekableLittleEndianDataInputStreamTest.java
new file mode 100644
index 00000000..c0b80b76
--- /dev/null
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocument_SeekableLittleEndianDataInputStreamTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2011, 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.*;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+
+/**
+ * CompoundDocument_SeekableLittleEndianDataInputStreamTestCase
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: CompoundDocument_SeekableLittleEndianDataInputStreamTestCase.java,v 1.0 18.10.11 16:35 haraldk Exp$
+ */
+public class CompoundDocument_SeekableLittleEndianDataInputStreamTest extends InputStreamAbstractTest implements SeekableInterfaceTest {
+ private final SeekableInterfaceTest seekableTest = new SeekableAbstractTest() {
+ @Override
+ protected Seekable createSeekable() {
+ return (Seekable) makeInputStream();
+ }
+ };
+
+ @Override
+ protected CompoundDocument.SeekableLittleEndianDataInputStream makeInputStream(byte[] pBytes) {
+ return new CompoundDocument.SeekableLittleEndianDataInputStream(new MemoryCacheSeekableStream(new ByteArrayInputStream(pBytes)));
+ }
+
+ @Test
+ public void testSeekable() {
+ seekableTest.testSeekable();
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocument_SeekableLittleEndianDataInputStreamTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocument_SeekableLittleEndianDataInputStreamTestCase.java
deleted file mode 100644
index d4e426d6..00000000
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocument_SeekableLittleEndianDataInputStreamTestCase.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2011, 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 "TwelveMonkeys" 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 OWNER 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.*;
-import org.junit.Test;
-
-import java.io.ByteArrayInputStream;
-
-/**
- * CompoundDocument_SeekableLittleEndianDataInputStreamTestCase
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haraldk$
- * @version $Id: CompoundDocument_SeekableLittleEndianDataInputStreamTestCase.java,v 1.0 18.10.11 16:35 haraldk Exp$
- */
-public class CompoundDocument_SeekableLittleEndianDataInputStreamTestCase extends InputStreamAbstractTestCase implements SeekableInterfaceTest {
- private final SeekableInterfaceTest seekableTest = new SeekableAbstractTestCase() {
- @Override
- protected Seekable createSeekable() {
- return (Seekable) makeInputStream();
- }
- };
-
- @Override
- protected CompoundDocument.SeekableLittleEndianDataInputStream makeInputStream(byte[] pBytes) {
- return new CompoundDocument.SeekableLittleEndianDataInputStream(new MemoryCacheSeekableStream(new ByteArrayInputStream(pBytes)));
- }
-
- @Test
- public void testSeekable() {
- seekableTest.testSeekable();
- }
-}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocument_StreamTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocument_StreamTest.java
similarity index 73%
rename from common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocument_StreamTestCase.java
rename to common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocument_StreamTest.java
index 14039eae..6bdd984a 100644
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocument_StreamTestCase.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocument_StreamTest.java
@@ -4,39 +4,41 @@
*
* 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 "TwelveMonkeys" 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 OWNER 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.
+ * * 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.InputStreamAbstractTestCase;
+import com.twelvemonkeys.io.InputStreamAbstractTest;
import com.twelvemonkeys.io.LittleEndianDataOutputStream;
import com.twelvemonkeys.io.MemoryCacheSeekableStream;
-import com.twelvemonkeys.io.SeekableInputStream;
import org.junit.Test;
-import java.io.*;
-import java.net.URISyntaxException;
-import java.net.URL;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Arrays;
@@ -49,35 +51,7 @@ import static org.junit.Assert.*;
* @author last modified by $Author: haraldk$
* @version $Id: CompoundDocument_StreamTestCase.java,v 1.0 13.10.11 12:01 haraldk Exp$
*/
-//@Ignore("Need proper in-memory creation of CompoundDocuments")
-public class CompoundDocument_StreamTestCase extends InputStreamAbstractTestCase {
- private static final String SAMPLE_DATA = "/Thumbs-camera.db";
-
- protected final CompoundDocument createTestDocument() throws IOException {
- URL input = getClass().getResource(SAMPLE_DATA);
-
- assertNotNull("Missing test resource!", input);
- assertEquals("Test resource not a file:// resource", "file", input.getProtocol());
-
- try {
- return new CompoundDocument(new File(input.toURI()));
- }
- catch (URISyntaxException e) {
- throw new AssertionError(e);
- }
- }
-
- private SeekableInputStream createRealInputStream() {
- try {
- Entry first = createTestDocument().getRootEntry().getChildEntries().first();
- assertNotNull(first);
- return first.getInputStream();
- }
- catch (IOException e) {
- throw new AssertionError(e);
- }
- }
-
+public class CompoundDocument_StreamTest extends InputStreamAbstractTest {
@Override
protected InputStream makeInputStream(byte[] data) {
try {
@@ -180,15 +154,13 @@ public class CompoundDocument_StreamTestCase extends InputStreamAbstractTestCase
return pad;
}
-// @Ignore
@Test
- public void testDev() throws IOException {
+ public void testStreamRead() throws IOException {
InputStream stream = makeInputStream(makeOrderedArray(32));
int read;
int count = 0;
while ((read = stream.read()) >= 0) {
-// System.out.printf("read %02d: 0x%02x%n", count, read & 0xFF);
assertEquals(count, read);
count++;
}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/net/HTTPUtilTest.java b/common/common-io/src/test/java/com/twelvemonkeys/net/HTTPUtilTest.java
index 9bb83f53..f3912e92 100644
--- a/common/common-io/src/test/java/com/twelvemonkeys/net/HTTPUtilTest.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/net/HTTPUtilTest.java
@@ -4,33 +4,35 @@
*
* 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 "TwelveMonkeys" 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 OWNER 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.
+ * * 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 org.junit.Test;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
/**
* HTTPUtilTest
diff --git a/common/common-lang/pom.xml b/common/common-lang/pom.xml
index 79316441..8fd46cb7 100644
--- a/common/common-lang/pom.xml
+++ b/common/common-lang/pom.xml
@@ -4,7 +4,7 @@
com.twelvemonkeys.common
common
- 3.4-SNAPSHOT
+ 3.8.0-SNAPSHOT
common-lang
jar
@@ -13,4 +13,8 @@
The TwelveMonkeys Common Language support
+
+ com.twelvemonkeys.common.lang
+
+
diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/lang/BeanUtil.java b/common/common-lang/src/main/java/com/twelvemonkeys/lang/BeanUtil.java
index 7eae35dd..b6c6a4a1 100755
--- a/common/common-lang/src/main/java/com/twelvemonkeys/lang/BeanUtil.java
+++ b/common/common-lang/src/main/java/com/twelvemonkeys/lang/BeanUtil.java
@@ -1,603 +1,607 @@
-/*
- * 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 "TwelveMonkeys" 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 OWNER 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.lang;
-
-import com.twelvemonkeys.util.convert.ConversionException;
-import com.twelvemonkeys.util.convert.Converter;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Map;
-import java.util.Arrays;
-
-/**
- * A utility class with some useful bean-related functions.
- *
- * NOTE: This class is not considered part of the public API and may be changed without notice
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/BeanUtil.java#2 $
- */
-public final class BeanUtil {
-
- // Disallow creating objects of this type
- private BeanUtil() {
- }
-
- /**
- * Gets a property value from the given object, using reflection.
- * Now supports getting values from properties of properties
- * (recursive).
- *
- * @param pObject The object to get the property from
- * @param pProperty The name of the property
- *
- * @return A string containing the value of the given property, or {@code null}
- * if it can not be found.
- * @todo Remove System.err's... Create new Exception? Hmm..
- */
- public static Object getPropertyValue(Object pObject, String pProperty) {
- //
- // TODO: Support get(Object) method of Collections!
- // Handle lists and arrays with [] (index) operator
- //
-
- if (pObject == null || pProperty == null || pProperty.length() < 1) {
- return null;
- }
-
- Class> objClass = pObject.getClass();
-
- Object result = pObject;
-
- // Method for method...
- String subProp;
- int begIdx = 0;
- int endIdx = begIdx;
-
- while (begIdx < pProperty.length() && begIdx >= 0) {
-
- endIdx = pProperty.indexOf(".", endIdx + 1);
- if (endIdx > 0) {
- subProp = pProperty.substring(begIdx, endIdx);
- begIdx = endIdx + 1;
- }
- else {
- // The final property!
- // If there's just the first-level property, subProp will be
- // equal to property
- subProp = pProperty.substring(begIdx);
- begIdx = -1;
- }
-
- // Check for "[" and "]"
- Object[] param = null;
- Class[] paramClass = new Class[0];
-
- int begBracket;
- if ((begBracket = subProp.indexOf("[")) > 0) {
- // An error if there is no matching bracket
- if (!subProp.endsWith("]")) {
- return null;
- }
-
- String between = subProp.substring(begBracket + 1,
- subProp.length() - 1);
- subProp = subProp.substring(0, begBracket);
-
- // If brackets exist, check type of argument between brackets
- param = new Object[1];
- paramClass = new Class[1];
-
- //try {
- // TODO: isNumber returns true, even if too big for integer...
- if (StringUtil.isNumber(between)) {
- // We have a number
- // Integer -> array subscript -> getXXX(int i)
- try {
- // Insert param and it's Class
- param[0] = Integer.valueOf(between);
- paramClass[0] = Integer.TYPE; // int.class
- }
- catch (NumberFormatException e) {
- // ??
- // Probably too small or too large value..
- }
- }
- else {
- //catch (NumberFormatException e) {
- // Not a number... Try String
- // String -> Hashtable key -> getXXX(String str)
- // Insert param and it's Class
- param[0] = between.toLowerCase();
- paramClass[0] = String.class;
- }
- }
-
- Method method;
- String methodName = "get" + StringUtil.capitalize(subProp);
- try {
- // Try to get the "get" method for the given property
- method = objClass.getMethod(methodName, paramClass);
- }
- catch (NoSuchMethodException e) {
- System.err.print("No method named \"" + methodName + "()\"");
- // The array might be of size 0...
- if (paramClass.length > 0 && paramClass[0] != null) {
- System.err.print(" with the parameter " + paramClass[0].getName());
- }
-
- System.err.println(" in class " + objClass.getName() + "!");
- return null;
- }
-
- // If method for some reason should be null, give up
- if (method == null) {
- return null;
- }
-
- try {
- // We have a method, try to invoke it
- // The resutling object will be either the property we are
- // Looking for, or the parent
-
- // System.err.println("Trying " + objClass.getName() + "." + method.getName() + "(" + ((param != null && param.length > 0) ? param[0] : "") + ")");
- result = method.invoke(result, param);
- }
- catch (InvocationTargetException e) {
- System.err.println("property=" + pProperty + " & result=" + result + " & param=" + Arrays.toString(param));
- e.getTargetException().printStackTrace();
- e.printStackTrace();
- return null;
- }
- catch (IllegalAccessException e) {
- e.printStackTrace();
- return null;
- }
- catch (NullPointerException e) {
- System.err.println(objClass.getName() + "." + method.getName() + "(" + ((paramClass.length > 0 && paramClass[0] != null) ? paramClass[0].getName() : "") + ")");
- e.printStackTrace();
- return null;
- }
-
- if (result != null) {
- // Get the class of the reulting object
- objClass = result.getClass();
- }
- else {
- return null;
- }
- } // while
-
- return result;
- }
-
- /**
- * Sets the property value to an object using reflection.
- * Supports setting values of properties that are properties of
- * properties (recursive).
- *
- * @param pObject The object to get a property from
- * @param pProperty The name of the property
- * @param pValue The property value
- *
- * @throws NoSuchMethodException if there's no write method for the
- * given property
- * @throws InvocationTargetException if invoking the write method failed
- * @throws IllegalAccessException if the caller class has no access to the
- * write method
- */
- public static void setPropertyValue(Object pObject, String pProperty, Object pValue)
- throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
-
- //
- // TODO: Support set(Object, Object)/put(Object, Object) methods
- // of Collections!
- // Handle lists and arrays with [] (index) operator
-
- Class paramType = pValue != null ? pValue.getClass() : Object.class;
-
- // Preserve references
- Object obj = pObject;
- String property = pProperty;
-
- // Recurse and find real parent if property contains a '.'
- int dotIdx = property.indexOf('.');
- if (dotIdx >= 0) {
- // Get real parent
- obj = getPropertyValue(obj, property.substring(0, dotIdx));
- // Get the property of the parent
- property = property.substring(dotIdx + 1);
- }
-
- // Find method
- Object[] params = {pValue};
- Method method = getMethodMayModifyParams(obj, "set" + StringUtil.capitalize(property),
- new Class[] {paramType}, params);
-
- // Invoke it
- method.invoke(obj, params);
- }
-
- private static Method getMethodMayModifyParams(Object pObject, String pName, Class[] pParams, Object[] pValues)
- throws NoSuchMethodException {
- // NOTE: This method assumes pParams.length == 1 && pValues.length == 1
-
- Method method = null;
- Class paramType = pParams[0];
-
- try {
- method = pObject.getClass().getMethod(pName, pParams);
- }
- catch (NoSuchMethodException e) {
- // No direct match
-
- // 1: If primitive wrapper, try unwrap conversion first
- /*if (paramType.isPrimitive()) { // NOTE: Can't be primitive type
- params[0] = ReflectUtil.wrapType(paramType);
- }
- else*/ if (ReflectUtil.isPrimitiveWrapper(paramType)) {
- pParams[0] = ReflectUtil.unwrapType(paramType);
- }
-
- try {
- // If this does not throw an exception, it works
- method = pObject.getClass().getMethod(pName, pParams);
- }
- catch (Throwable t) {
- // Ignore
- }
-
- // 2: Try any super-types of paramType, to see if we have a match
- if (method == null) {
- while ((paramType = paramType.getSuperclass()) != null) {
- pParams[0] = paramType;
- try {
- // If this does not throw an exception, it works
- method = pObject.getClass().getMethod(pName, pParams);
- }
- catch (Throwable t) {
- // Ignore/Continue
- continue;
- }
-
- break;
- }
- }
-
- // 3: Try to find a different method with the same name, that has
- // a parameter type we can convert to...
- // NOTE: There's no ordering here..
- // TODO: Should we try to do that? What would the ordering be?
- if (method == null) {
- Method[] methods = pObject.getClass().getMethods();
- for (Method candidate : methods) {
- if (Modifier.isPublic(candidate.getModifiers()) && candidate.getName().equals(pName)
- && candidate.getReturnType() == Void.TYPE && candidate.getParameterTypes().length == 1) {
- // NOTE: Assumes paramTypes.length == 1
-
- Class type = candidate.getParameterTypes()[0];
-
- try {
- pValues[0] = convertValueToType(pValues[0], type);
- }
- catch (Throwable t) {
- continue;
- }
-
- // We were able to convert the parameter, let's try
- method = candidate;
- break;
- }
- }
- }
-
- // Give up...
- if (method == null) {
- throw e;
- }
- }
- return method;
- }
-
- private static Object convertValueToType(Object pValue, Class> pType) throws ConversionException {
- if (pType.isPrimitive()) {
- if (pType == Boolean.TYPE && pValue instanceof Boolean) {
- return pValue;
- }
- else if (pType == Byte.TYPE && pValue instanceof Byte) {
- return pValue;
- }
- else if (pType == Character.TYPE && pValue instanceof Character) {
- return pValue;
- }
- else if (pType == Double.TYPE && pValue instanceof Double) {
- return pValue;
- }
- else if (pType == Float.TYPE && pValue instanceof Float) {
- return pValue;
- }
- else if (pType == Integer.TYPE && pValue instanceof Integer) {
- return pValue;
- }
- else if (pType == Long.TYPE && pValue instanceof Long) {
- return pValue;
- }
- else if (pType == Short.TYPE && pValue instanceof Short) {
- return pValue;
- }
- }
-
- // TODO: Convert value to single-value array if needed
- // TODO: Convert CSV String to string array (or potentially any type of array)
-
- // TODO: Convert other types
- if (pValue instanceof String) {
- Converter converter = Converter.getInstance();
- return converter.toObject((String) pValue, pType);
- }
- else if (pType == String.class) {
- Converter converter = Converter.getInstance();
- return converter.toString(pValue);
- }
- else {
- throw new ConversionException("Cannot convert " + pValue.getClass().getName() + " to " + pType.getName());
- }
- }
-
- /**
- * Creates an object from the given class' single argument constructor.
- *
- * @param pClass The class to create instance from
- * @param pParam The parameters to the constructor
- *
- * @return The object created from the constructor.
- * If the constructor could not be invoked for any reason, null is
- * returned.
- *
- * @throws InvocationTargetException if the constructor failed
- */
- // TODO: Move to ReflectUtil
- public static T createInstance(Class pClass, Object pParam)
- throws InvocationTargetException {
- return createInstance(pClass, new Object[] {pParam});
- }
-
- /**
- * Creates an object from the given class' constructor that matches
- * the given paramaters.
- *
- * @param pClass The class to create instance from
- * @param pParams The parameters to the constructor
- *
- * @return The object created from the constructor.
- * If the constructor could not be invoked for any reason, null is
- * returned.
- *
- * @throws InvocationTargetException if the constructor failed
- */
- // TODO: Move to ReflectUtil
- public static T createInstance(Class pClass, Object... pParams)
- throws InvocationTargetException {
- T value;
-
- try {
- // Create param and argument arrays
- Class[] paramTypes = null;
- if (pParams != null && pParams.length > 0) {
- paramTypes = new Class[pParams.length];
- for (int i = 0; i < pParams.length; i++) {
- paramTypes[i] = pParams[i].getClass();
- }
- }
-
- // Get constructor
- Constructor constructor = pClass.getConstructor(paramTypes);
-
- // Invoke and create instance
- value = constructor.newInstance(pParams);
- }
- /* All this to let InvocationTargetException pass on */
- catch (NoSuchMethodException nsme) {
- return null;
- }
- catch (IllegalAccessException iae) {
- return null;
- }
- catch (IllegalArgumentException iarge) {
- return null;
- }
- catch (InstantiationException ie) {
- return null;
- }
- catch (ExceptionInInitializerError err) {
- return null;
- }
-
- return value;
- }
-
- /**
- * Gets an object from any given static method, with the given parameter.
- *
- * @param pClass The class to invoke method on
- * @param pMethod The name of the method to invoke
- * @param pParam The parameter to the method
- *
- * @return The object returned by the static method.
- * If the return type of the method is a primitive type, it is wrapped in
- * the corresponding wrapper object (int is wrapped in an Integer).
- * If the return type of the method is void, null is returned.
- * If the method could not be invoked for any reason, null is returned.
- *
- * @throws InvocationTargetException if the invocation failed
- */
- // TODO: Move to ReflectUtil
- // TODO: Rename to invokeStatic?
- public static Object invokeStaticMethod(Class> pClass, String pMethod, Object pParam)
- throws InvocationTargetException {
-
- return invokeStaticMethod(pClass, pMethod, new Object[] {pParam});
- }
-
- /**
- * Gets an object from any given static method, with the given parameter.
- *
- * @param pClass The class to invoke method on
- * @param pMethod The name of the method to invoke
- * @param pParams The parameters to the method
- *
- * @return The object returned by the static method.
- * If the return type of the method is a primitive type, it is wrapped in
- * the corresponding wrapper object (int is wrapped in an Integer).
- * If the return type of the method is void, null is returned.
- * If the method could not be invoked for any reason, null is returned.
- *
- * @throws InvocationTargetException if the invocation failed
- */
- // TODO: Move to ReflectUtil
- // TODO: Rename to invokeStatic?
- public static Object invokeStaticMethod(Class> pClass, String pMethod, Object... pParams)
- throws InvocationTargetException {
-
- Object value = null;
-
- try {
- // Create param and argument arrays
- Class[] paramTypes = new Class[pParams.length];
- for (int i = 0; i < pParams.length; i++) {
- paramTypes[i] = pParams[i].getClass();
- }
-
- // Get method
- // *** If more than one such method is found in the class, and one
- // of these methods has a RETURN TYPE that is more specific than
- // any of the others, that method is reflected; otherwise one of
- // the methods is chosen ARBITRARILY.
- // java/lang/Class.html#getMethod(java.lang.String, java.lang.Class[])
- Method method = pClass.getMethod(pMethod, paramTypes);
-
- // Invoke public static method
- if (Modifier.isPublic(method.getModifiers()) && Modifier.isStatic(method.getModifiers())) {
- value = method.invoke(null, pParams);
- }
-
- }
- /* All this to let InvocationTargetException pass on */
- catch (NoSuchMethodException nsme) {
- return null;
- }
- catch (IllegalAccessException iae) {
- return null;
- }
- catch (IllegalArgumentException iarge) {
- return null;
- }
-
- return value;
- }
-
- /**
- * Configures the bean according to the given mapping.
- * For each {@code Map.Entry} in {@code Map.values()},
- * a method named
- * {@code set + capitalize(entry.getKey())} is called on the bean,
- * with {@code entry.getValue()} as its argument.
- *
- * Properties that has no matching set-method in the bean, are simply
- * discarded.
- *
- * @param pBean The bean to configure
- * @param pMapping The mapping for the bean
- *
- * @throws NullPointerException if any of the parameters are null.
- * @throws InvocationTargetException if an error occurs when invoking the
- * setter-method.
- */
- // TODO: Add a version that takes a ConfigurationErrorListener callback interface
- // TODO: ...or a boolean pFailOnError parameter
- // TODO: ...or return Exceptions as an array?!
- // TODO: ...or something whatsoever that makes clients able to determine something's not right
- public static void configure(final Object pBean, final Map pMapping) throws InvocationTargetException {
- configure(pBean, pMapping, false);
- }
-
- /**
- * Configures the bean according to the given mapping.
- * For each {@code Map.Entry} in {@code Map.values()},
- * a method named
- * {@code set + capitalize(entry.getKey())} is called on the bean,
- * with {@code entry.getValue()} as its argument.
- *
- * Optionally, lisp-style names are allowed, and automatically converted
- * to Java-style camel-case names.
- *
- * Properties that has no matching set-method in the bean, are simply
- * discarded.
- *
- * @see StringUtil#lispToCamel(String)
- *
- * @param pBean The bean to configure
- * @param pMapping The mapping for the bean
- * @param pLispToCamel Allow lisp-style names, and automatically convert
- * them to Java-style camel-case.
- *
- * @throws NullPointerException if any of the parameters are null.
- * @throws InvocationTargetException if an error occurs when invoking the
- * setter-method.
- */
- public static void configure(final Object pBean, final Map pMapping, final boolean pLispToCamel) throws InvocationTargetException {
- // Loop over properties in mapping
- for (final Map.Entry entry : pMapping.entrySet()) {
- try {
- // Configure each property in turn
- final String property = StringUtil.valueOf(entry.getKey());
- try {
- setPropertyValue(pBean, property, entry.getValue());
- }
- catch (NoSuchMethodException ignore) {
- // If invocation failed, convert lisp-style and try again
- if (pLispToCamel && property.indexOf('-') > 0) {
- setPropertyValue(pBean, StringUtil.lispToCamel(property, false), entry.getValue());
- }
- }
- }
- catch (NoSuchMethodException nsme) {
- // This property was not configured
- }
- catch (IllegalAccessException iae) {
- // This property was not configured
- }
- }
- }
-}
+/*
+ * 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.lang;
+
+import com.twelvemonkeys.util.convert.ConversionException;
+import com.twelvemonkeys.util.convert.Converter;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * A utility class with some useful bean-related functions.
+ *
+ * NOTE: This class is not considered part of the public API and may be changed without notice
+ *
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/BeanUtil.java#2 $
+ */
+public final class BeanUtil {
+
+ // Disallow creating objects of this type
+ private BeanUtil() {
+ }
+
+ /**
+ * Gets a property value from the given object, using reflection.
+ * Now supports getting values from properties of properties
+ * (recursive).
+ *
+ * @param pObject The object to get the property from
+ * @param pProperty The name of the property
+ *
+ * @return A string containing the value of the given property, or {@code null}
+ * if it can not be found.
+ */
+ public static Object getPropertyValue(Object pObject, String pProperty) {
+ // TODO: Remove System.err's... Create new Exception? Hmm..
+ // TODO: Support get(Object) method of Collections!
+ // Handle lists and arrays with [] (index) operator
+
+ if (pObject == null || pProperty == null || pProperty.length() < 1) {
+ return null;
+ }
+
+ Class> objClass = pObject.getClass();
+
+ Object result = pObject;
+
+ // Method for method...
+ String subProp;
+ int begIdx = 0;
+ int endIdx = begIdx;
+
+ while (begIdx < pProperty.length() && begIdx >= 0) {
+
+ endIdx = pProperty.indexOf('.', endIdx + 1);
+ if (endIdx > 0) {
+ subProp = pProperty.substring(begIdx, endIdx);
+ begIdx = endIdx + 1;
+ }
+ else {
+ // The final property!
+ // If there's just the first-level property, subProp will be
+ // equal to property
+ subProp = pProperty.substring(begIdx);
+ begIdx = -1;
+ }
+
+ // Check for "[" and "]"
+ Object[] param = null;
+ Class[] paramClass = new Class[0];
+
+ int begBracket;
+ if ((begBracket = subProp.indexOf('[')) > 0) {
+ // An error if there is no matching bracket
+ if (!subProp.endsWith("]")) {
+ return null;
+ }
+
+ String between = subProp.substring(begBracket + 1,
+ subProp.length() - 1);
+ subProp = subProp.substring(0, begBracket);
+
+ // If brackets exist, check type of argument between brackets
+ param = new Object[1];
+ paramClass = new Class[1];
+
+ //try {
+ // TODO: isNumber returns true, even if too big for integer...
+ if (StringUtil.isNumber(between)) {
+ // We have a number
+ // Integer -> array subscript -> getXXX(int i)
+ try {
+ // Insert param and it's Class
+ param[0] = Integer.valueOf(between);
+ paramClass[0] = Integer.TYPE; // int.class
+ }
+ catch (NumberFormatException e) {
+ // ??
+ // Probably too small or too large value..
+ }
+ }
+ else {
+ //catch (NumberFormatException e) {
+ // Not a number... Try String
+ // String -> Hashtable key -> getXXX(String str)
+ // Insert param and it's Class
+ param[0] = between.toLowerCase();
+ paramClass[0] = String.class;
+ }
+ }
+
+ Method method;
+ String methodName = "get" + StringUtil.capitalize(subProp);
+ try {
+ // Try to get the "get" method for the given property
+ method = objClass.getMethod(methodName, paramClass);
+ }
+ catch (NoSuchMethodException e) {
+ System.err.print("No method named \"" + methodName + "()\"");
+ // The array might be of size 0...
+ if (paramClass.length > 0 && paramClass[0] != null) {
+ System.err.print(" with the parameter " + paramClass[0].getName());
+ }
+
+ System.err.println(" in class " + objClass.getName() + "!");
+ return null;
+ }
+
+ // If method for some reason should be null, give up
+ if (method == null) {
+ return null;
+ }
+
+ try {
+ // We have a method, try to invoke it
+ // The resutling object will be either the property we are
+ // Looking for, or the parent
+
+ // System.err.println("Trying " + objClass.getName() + "." + method.getName() + "(" + ((param != null && param.length > 0) ? param[0] : "") + ")");
+ result = method.invoke(result, param);
+ }
+ catch (InvocationTargetException e) {
+ System.err.println("property=" + pProperty + " & result=" + result + " & param=" + Arrays.toString(param));
+ e.getTargetException().printStackTrace();
+ e.printStackTrace();
+ return null;
+ }
+ catch (IllegalAccessException e) {
+ e.printStackTrace();
+ return null;
+ }
+ catch (NullPointerException e) {
+ System.err.println(objClass.getName() + "." + method.getName() + "(" + ((paramClass.length > 0 && paramClass[0] != null) ? paramClass[0].getName() : "") + ")");
+ e.printStackTrace();
+ return null;
+ }
+
+ if (result != null) {
+ // Get the class of the reulting object
+ objClass = result.getClass();
+ }
+ else {
+ return null;
+ }
+ } // while
+
+ return result;
+ }
+
+ /**
+ * Sets the property value to an object using reflection.
+ * Supports setting values of properties that are properties of
+ * properties (recursive).
+ *
+ * @param pObject The object to get a property from
+ * @param pProperty The name of the property
+ * @param pValue The property value
+ *
+ * @throws NoSuchMethodException if there's no write method for the
+ * given property
+ * @throws InvocationTargetException if invoking the write method failed
+ * @throws IllegalAccessException if the caller class has no access to the
+ * write method
+ */
+ public static void setPropertyValue(Object pObject, String pProperty, Object pValue)
+ throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+
+ //
+ // TODO: Support set(Object, Object)/put(Object, Object) methods
+ // of Collections!
+ // Handle lists and arrays with [] (index) operator
+
+ Class paramType = pValue != null ? pValue.getClass() : Object.class;
+
+ // Preserve references
+ Object obj = pObject;
+ String property = pProperty;
+
+ // Recurse and find real parent if property contains a '.'
+ int dotIdx = property.indexOf('.');
+ if (dotIdx >= 0) {
+ // Get real parent
+ obj = getPropertyValue(obj, property.substring(0, dotIdx));
+ // Get the property of the parent
+ property = property.substring(dotIdx + 1);
+ }
+
+ // Find method
+ Object[] params = {pValue};
+ Method method = getMethodMayModifyParams(obj, "set" + StringUtil.capitalize(property),
+ new Class[] {paramType}, params);
+
+ // Invoke it
+ method.invoke(obj, params);
+ }
+
+ private static Method getMethodMayModifyParams(Object pObject, String pName, Class[] pParams, Object[] pValues)
+ throws NoSuchMethodException {
+ // NOTE: This method assumes pParams.length == 1 && pValues.length == 1
+
+ Method method = null;
+ Class paramType = pParams[0];
+
+ try {
+ method = pObject.getClass().getMethod(pName, pParams);
+ }
+ catch (NoSuchMethodException e) {
+ // No direct match
+
+ // 1: If primitive wrapper, try unwrap conversion first
+ /*if (paramType.isPrimitive()) { // NOTE: Can't be primitive type
+ params[0] = ReflectUtil.wrapType(paramType);
+ }
+ else*/ if (ReflectUtil.isPrimitiveWrapper(paramType)) {
+ pParams[0] = ReflectUtil.unwrapType(paramType);
+ }
+
+ try {
+ // If this does not throw an exception, it works
+ method = pObject.getClass().getMethod(pName, pParams);
+ }
+ catch (Throwable t) {
+ // Ignore
+ }
+
+ // 2: Try any super-types of paramType, to see if we have a match
+ if (method == null) {
+ while ((paramType = paramType.getSuperclass()) != null) {
+ pParams[0] = paramType;
+ try {
+ // If this does not throw an exception, it works
+ method = pObject.getClass().getMethod(pName, pParams);
+ }
+ catch (Throwable t) {
+ // Ignore/Continue
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ // 3: Try to find a different method with the same name, that has
+ // a parameter type we can convert to...
+ // NOTE: There's no ordering here..
+ // TODO: Should we try to do that? What would the ordering be?
+ if (method == null) {
+ Method[] methods = pObject.getClass().getMethods();
+ for (Method candidate : methods) {
+ if (Modifier.isPublic(candidate.getModifiers()) && candidate.getName().equals(pName)
+ && candidate.getReturnType() == Void.TYPE && candidate.getParameterTypes().length == 1) {
+ // NOTE: Assumes paramTypes.length == 1
+
+ Class type = candidate.getParameterTypes()[0];
+
+ try {
+ pValues[0] = convertValueToType(pValues[0], type);
+ }
+ catch (Throwable t) {
+ continue;
+ }
+
+ // We were able to convert the parameter, let's try
+ method = candidate;
+ break;
+ }
+ }
+ }
+
+ // Give up...
+ if (method == null) {
+ throw e;
+ }
+ }
+ return method;
+ }
+
+ private static Object convertValueToType(Object pValue, Class> pType) throws ConversionException {
+ if (pType.isPrimitive()) {
+ if (pType == Boolean.TYPE && pValue instanceof Boolean) {
+ return pValue;
+ }
+ else if (pType == Byte.TYPE && pValue instanceof Byte) {
+ return pValue;
+ }
+ else if (pType == Character.TYPE && pValue instanceof Character) {
+ return pValue;
+ }
+ else if (pType == Double.TYPE && pValue instanceof Double) {
+ return pValue;
+ }
+ else if (pType == Float.TYPE && pValue instanceof Float) {
+ return pValue;
+ }
+ else if (pType == Integer.TYPE && pValue instanceof Integer) {
+ return pValue;
+ }
+ else if (pType == Long.TYPE && pValue instanceof Long) {
+ return pValue;
+ }
+ else if (pType == Short.TYPE && pValue instanceof Short) {
+ return pValue;
+ }
+ }
+
+ // TODO: Convert value to single-value array if needed
+ // TODO: Convert CSV String to string array (or potentially any type of array)
+
+ // TODO: Convert other types
+ if (pValue instanceof String) {
+ Converter converter = Converter.getInstance();
+ return converter.toObject((String) pValue, pType);
+ }
+ else if (pType == String.class) {
+ Converter converter = Converter.getInstance();
+ return converter.toString(pValue);
+ }
+ else {
+ throw new ConversionException("Cannot convert " + pValue.getClass().getName() + " to " + pType.getName());
+ }
+ }
+
+ /**
+ * Creates an object from the given class' single argument constructor.
+ *
+ * @param pClass The class to create instance from
+ * @param pParam The parameters to the constructor
+ *
+ * @return The object created from the constructor.
+ * If the constructor could not be invoked for any reason, null is
+ * returned.
+ *
+ * @throws InvocationTargetException if the constructor failed
+ */
+ // TODO: Move to ReflectUtil
+ public static T createInstance(Class pClass, Object pParam)
+ throws InvocationTargetException {
+ return createInstance(pClass, new Object[] {pParam});
+ }
+
+ /**
+ * Creates an object from the given class' constructor that matches
+ * the given paramaters.
+ *
+ * @param pClass The class to create instance from
+ * @param pParams The parameters to the constructor
+ *
+ * @return The object created from the constructor.
+ * If the constructor could not be invoked for any reason, null is
+ * returned.
+ *
+ * @throws InvocationTargetException if the constructor failed
+ */
+ // TODO: Move to ReflectUtil
+ public static T createInstance(Class pClass, Object... pParams)
+ throws InvocationTargetException {
+ T value;
+
+ try {
+ // Create param and argument arrays
+ Class[] paramTypes = null;
+ if (pParams != null && pParams.length > 0) {
+ paramTypes = new Class[pParams.length];
+ for (int i = 0; i < pParams.length; i++) {
+ paramTypes[i] = pParams[i].getClass();
+ }
+ }
+
+ // Get constructor
+ Constructor constructor = pClass.getConstructor(paramTypes);
+
+ // Invoke and create instance
+ value = constructor.newInstance(pParams);
+ }
+ /* All this to let InvocationTargetException pass on */
+ catch (NoSuchMethodException nsme) {
+ return null;
+ }
+ catch (IllegalAccessException iae) {
+ return null;
+ }
+ catch (IllegalArgumentException iarge) {
+ return null;
+ }
+ catch (InstantiationException ie) {
+ return null;
+ }
+ catch (ExceptionInInitializerError err) {
+ return null;
+ }
+
+ return value;
+ }
+
+ /**
+ * Gets an object from any given static method, with the given parameter.
+ *
+ * @param pClass The class to invoke method on
+ * @param pMethod The name of the method to invoke
+ * @param pParam The parameter to the method
+ *
+ * @return The object returned by the static method.
+ * If the return type of the method is a primitive type, it is wrapped in
+ * the corresponding wrapper object (int is wrapped in an Integer).
+ * If the return type of the method is void, null is returned.
+ * If the method could not be invoked for any reason, null is returned.
+ *
+ * @throws InvocationTargetException if the invocation failed
+ */
+ // TODO: Move to ReflectUtil
+ // TODO: Rename to invokeStatic?
+ public static Object invokeStaticMethod(Class> pClass, String pMethod, Object pParam)
+ throws InvocationTargetException {
+
+ return invokeStaticMethod(pClass, pMethod, new Object[] {pParam});
+ }
+
+ /**
+ * Gets an object from any given static method, with the given parameter.
+ *
+ * @param pClass The class to invoke method on
+ * @param pMethod The name of the method to invoke
+ * @param pParams The parameters to the method
+ *
+ * @return The object returned by the static method.
+ * If the return type of the method is a primitive type, it is wrapped in
+ * the corresponding wrapper object (int is wrapped in an Integer).
+ * If the return type of the method is void, null is returned.
+ * If the method could not be invoked for any reason, null is returned.
+ *
+ * @throws InvocationTargetException if the invocation failed
+ */
+ // TODO: Move to ReflectUtil
+ // TODO: Rename to invokeStatic?
+ public static Object invokeStaticMethod(Class> pClass, String pMethod, Object... pParams)
+ throws InvocationTargetException {
+
+ Object value = null;
+
+ try {
+ // Create param and argument arrays
+ Class[] paramTypes = new Class[pParams.length];
+ for (int i = 0; i < pParams.length; i++) {
+ paramTypes[i] = pParams[i].getClass();
+ }
+
+ // Get method
+ // *** If more than one such method is found in the class, and one
+ // of these methods has a RETURN TYPE that is more specific than
+ // any of the others, that method is reflected; otherwise one of
+ // the methods is chosen ARBITRARILY.
+ // java/lang/Class.html#getMethod(java.lang.String, java.lang.Class[])
+ Method method = pClass.getMethod(pMethod, paramTypes);
+
+ // Invoke public static method
+ if (Modifier.isPublic(method.getModifiers()) && Modifier.isStatic(method.getModifiers())) {
+ value = method.invoke(null, pParams);
+ }
+
+ }
+ /* All this to let InvocationTargetException pass on */
+ catch (NoSuchMethodException nsme) {
+ return null;
+ }
+ catch (IllegalAccessException iae) {
+ return null;
+ }
+ catch (IllegalArgumentException iarge) {
+ return null;
+ }
+
+ return value;
+ }
+
+ /**
+ * Configures the bean according to the given mapping.
+ * For each {@code Map.Entry} in {@code Map.values()},
+ * a method named
+ * {@code set + capitalize(entry.getKey())} is called on the bean,
+ * with {@code entry.getValue()} as its argument.
+ *
+ * Properties that has no matching set-method in the bean, are simply
+ * discarded.
+ *
+ *
+ * @param pBean The bean to configure
+ * @param pMapping The mapping for the bean
+ *
+ * @throws NullPointerException if any of the parameters are null.
+ * @throws InvocationTargetException if an error occurs when invoking the
+ * setter-method.
+ */
+ // TODO: Add a version that takes a ConfigurationErrorListener callback interface
+ // TODO: ...or a boolean pFailOnError parameter
+ // TODO: ...or return Exceptions as an array?!
+ // TODO: ...or something whatsoever that makes clients able to determine something's not right
+ public static void configure(final Object pBean, final Map pMapping) throws InvocationTargetException {
+ configure(pBean, pMapping, false);
+ }
+
+ /**
+ * Configures the bean according to the given mapping.
+ * For each {@code Map.Entry} in {@code Map.values()},
+ * a method named
+ * {@code set + capitalize(entry.getKey())} is called on the bean,
+ * with {@code entry.getValue()} as its argument.
+ *
+ * Optionally, lisp-style names are allowed, and automatically converted
+ * to Java-style camel-case names.
+ *
+ *
+ * Properties that has no matching set-method in the bean, are simply
+ * discarded.
+ *
+ *
+ * @see StringUtil#lispToCamel(String)
+ *
+ * @param pBean The bean to configure
+ * @param pMapping The mapping for the bean
+ * @param pLispToCamel Allow lisp-style names, and automatically convert
+ * them to Java-style camel-case.
+ *
+ * @throws NullPointerException if any of the parameters are null.
+ * @throws InvocationTargetException if an error occurs when invoking the
+ * setter-method.
+ */
+ public static void configure(final Object pBean, final Map pMapping, final boolean pLispToCamel) throws InvocationTargetException {
+ // Loop over properties in mapping
+ for (final Map.Entry entry : pMapping.entrySet()) {
+ try {
+ // Configure each property in turn
+ final String property = StringUtil.valueOf(entry.getKey());
+ try {
+ setPropertyValue(pBean, property, entry.getValue());
+ }
+ catch (NoSuchMethodException ignore) {
+ // If invocation failed, convert lisp-style and try again
+ if (pLispToCamel && property.indexOf('-') > 0) {
+ setPropertyValue(pBean, StringUtil.lispToCamel(property, false), entry.getValue());
+ }
+ }
+ }
+ catch (NoSuchMethodException nsme) {
+ // This property was not configured
+ }
+ catch (IllegalAccessException iae) {
+ // This property was not configured
+ }
+ }
+ }
+}
diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/lang/DateUtil.java b/common/common-lang/src/main/java/com/twelvemonkeys/lang/DateUtil.java
index 1234eb3c..92e262e3 100755
--- a/common/common-lang/src/main/java/com/twelvemonkeys/lang/DateUtil.java
+++ b/common/common-lang/src/main/java/com/twelvemonkeys/lang/DateUtil.java
@@ -1,202 +1,203 @@
-/*
- * 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 "TwelveMonkeys" 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 OWNER 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.lang;
-
-import java.util.Date;
-import java.util.TimeZone;
-
-/**
- * A utility class with useful date manipulation methods and constants.
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/DateUtil.java#1 $
- */
-public final class DateUtil {
-
- /** One second: 1000 milliseconds. */
- public static final long SECOND = 1000l;
-
- /** One minute: 60 seconds (60 000 milliseconds). */
- public static final long MINUTE = 60l * SECOND;
-
- /**
- * One hour: 60 minutes (3 600 000 milliseconds).
- * 60 minutes = 3 600 seconds = 3 600 000 milliseconds
- */
- public static final long HOUR = 60l * MINUTE;
-
- /**
- * One day: 24 hours (86 400 000 milliseconds).
- * 24 hours = 1 440 minutes = 86 400 seconds = 86 400 000 milliseconds.
- */
- public static final long DAY = 24l * HOUR;
-
- /**
- * One calendar year: 365.2425 days (31556952000 milliseconds).
- * 365.2425 days = 8765.82 hours = 525949.2 minutes = 31556952 seconds
- * = 31556952000 milliseconds.
- */
- public static final long CALENDAR_YEAR = 3652425l * 24l * 60l * 6l;
-
- private DateUtil() {
- }
-
- /**
- * Returns the time between the given start time and now (as defined by
- * {@link System#currentTimeMillis()}).
- *
- * @param pStart the start time
- *
- * @return the time between the given start time and now.
- */
- public static long delta(long pStart) {
- return System.currentTimeMillis() - pStart;
- }
-
- /**
- * Returns the time between the given start time and now (as defined by
- * {@link System#currentTimeMillis()}).
- *
- * @param pStart the start time
- *
- * @return the time between the given start time and now.
- */
- public static long delta(Date pStart) {
- return System.currentTimeMillis() - pStart.getTime();
- }
-
- /**
- * Gets the current time, rounded down to the closest second.
- * Equivalent to invoking
- * {@code roundToSecond(System.currentTimeMillis())}.
- *
- * @return the current time, rounded to the closest second.
- */
- public static long currentTimeSecond() {
- return roundToSecond(System.currentTimeMillis());
- }
-
- /**
- * Gets the current time, rounded down to the closest minute.
- * Equivalent to invoking
- * {@code roundToMinute(System.currentTimeMillis())}.
- *
- * @return the current time, rounded to the closest minute.
- */
- public static long currentTimeMinute() {
- return roundToMinute(System.currentTimeMillis());
- }
-
- /**
- * Gets the current time, rounded down to the closest hour.
- * Equivalent to invoking
- * {@code roundToHour(System.currentTimeMillis())}.
- *
- * @return the current time, rounded to the closest hour.
- */
- public static long currentTimeHour() {
- return roundToHour(System.currentTimeMillis());
- }
-
- /**
- * Gets the current time, rounded down to the closest day.
- * Equivalent to invoking
- * {@code roundToDay(System.currentTimeMillis())}.
- *
- * @return the current time, rounded to the closest day.
- */
- public static long currentTimeDay() {
- return roundToDay(System.currentTimeMillis());
- }
-
- /**
- * Rounds the given time down to the closest second.
- *
- * @param pTime time
- * @return the time rounded to the closest second.
- */
- public static long roundToSecond(final long pTime) {
- return (pTime / SECOND) * SECOND;
- }
-
- /**
- * Rounds the given time down to the closest minute.
- *
- * @param pTime time
- * @return the time rounded to the closest minute.
- */
- public static long roundToMinute(final long pTime) {
- return (pTime / MINUTE) * MINUTE;
- }
-
- /**
- * Rounds the given time down to the closest hour, using the default timezone.
- *
- * @param pTime time
- * @return the time rounded to the closest hour.
- */
- public static long roundToHour(final long pTime) {
- return roundToHour(pTime, TimeZone.getDefault());
- }
-
- /**
- * Rounds the given time down to the closest hour, using the given timezone.
- *
- * @param pTime time
- * @param pTimeZone the timezone to use when rounding
- * @return the time rounded to the closest hour.
- */
- public static long roundToHour(final long pTime, final TimeZone pTimeZone) {
- int offset = pTimeZone.getOffset(pTime);
- return ((pTime / HOUR) * HOUR) - offset;
- }
-
- /**
- * Rounds the given time down to the closest day, using the default timezone.
- *
- * @param pTime time
- * @return the time rounded to the closest day.
- */
- public static long roundToDay(final long pTime) {
- return roundToDay(pTime, TimeZone.getDefault());
- }
-
- /**
- * Rounds the given time down to the closest day, using the given timezone.
- *
- * @param pTime time
- * @param pTimeZone the timezone to use when rounding
- * @return the time rounded to the closest day.
- */
- public static long roundToDay(final long pTime, final TimeZone pTimeZone) {
- int offset = pTimeZone.getOffset(pTime);
- return (((pTime + offset) / DAY) * DAY) - 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.lang;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * A utility class with useful date manipulation methods and constants.
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/DateUtil.java#1 $
+ */
+public final class DateUtil {
+
+ /** One second: 1000 milliseconds. */
+ public static final long SECOND = 1000l;
+
+ /** One minute: 60 seconds (60 000 milliseconds). */
+ public static final long MINUTE = 60l * SECOND;
+
+ /**
+ * One hour: 60 minutes (3 600 000 milliseconds).
+ * 60 minutes = 3 600 seconds = 3 600 000 milliseconds
+ */
+ public static final long HOUR = 60l * MINUTE;
+
+ /**
+ * One day: 24 hours (86 400 000 milliseconds).
+ * 24 hours = 1 440 minutes = 86 400 seconds = 86 400 000 milliseconds.
+ */
+ public static final long DAY = 24l * HOUR;
+
+ /**
+ * One calendar year: 365.2425 days (31556952000 milliseconds).
+ * 365.2425 days = 8765.82 hours = 525949.2 minutes = 31556952 seconds
+ * = 31556952000 milliseconds.
+ */
+ public static final long CALENDAR_YEAR = 3652425l * 24l * 60l * 6l;
+
+ private DateUtil() {
+ }
+
+ /**
+ * Returns the time between the given start time and now (as defined by
+ * {@link System#currentTimeMillis()}).
+ *
+ * @param pStart the start time
+ *
+ * @return the time between the given start time and now.
+ */
+ public static long delta(long pStart) {
+ return System.currentTimeMillis() - pStart;
+ }
+
+ /**
+ * Returns the time between the given start time and now (as defined by
+ * {@link System#currentTimeMillis()}).
+ *
+ * @param pStart the start time
+ *
+ * @return the time between the given start time and now.
+ */
+ public static long delta(Date pStart) {
+ return System.currentTimeMillis() - pStart.getTime();
+ }
+
+ /**
+ * Gets the current time, rounded down to the closest second.
+ * Equivalent to invoking
+ * {@code roundToSecond(System.currentTimeMillis())}.
+ *
+ * @return the current time, rounded to the closest second.
+ */
+ public static long currentTimeSecond() {
+ return roundToSecond(System.currentTimeMillis());
+ }
+
+ /**
+ * Gets the current time, rounded down to the closest minute.
+ * Equivalent to invoking
+ * {@code roundToMinute(System.currentTimeMillis())}.
+ *
+ * @return the current time, rounded to the closest minute.
+ */
+ public static long currentTimeMinute() {
+ return roundToMinute(System.currentTimeMillis());
+ }
+
+ /**
+ * Gets the current time, rounded down to the closest hour.
+ * Equivalent to invoking
+ * {@code roundToHour(System.currentTimeMillis())}.
+ *
+ * @return the current time, rounded to the closest hour.
+ */
+ public static long currentTimeHour() {
+ return roundToHour(System.currentTimeMillis());
+ }
+
+ /**
+ * Gets the current time, rounded down to the closest day.
+ * Equivalent to invoking
+ * {@code roundToDay(System.currentTimeMillis())}.
+ *
+ * @return the current time, rounded to the closest day.
+ */
+ public static long currentTimeDay() {
+ return roundToDay(System.currentTimeMillis());
+ }
+
+ /**
+ * Rounds the given time down to the closest second.
+ *
+ * @param pTime time
+ * @return the time rounded to the closest second.
+ */
+ public static long roundToSecond(final long pTime) {
+ return (pTime / SECOND) * SECOND;
+ }
+
+ /**
+ * Rounds the given time down to the closest minute.
+ *
+ * @param pTime time
+ * @return the time rounded to the closest minute.
+ */
+ public static long roundToMinute(final long pTime) {
+ return (pTime / MINUTE) * MINUTE;
+ }
+
+ /**
+ * Rounds the given time down to the closest hour, using the default timezone.
+ *
+ * @param pTime time
+ * @return the time rounded to the closest hour.
+ */
+ public static long roundToHour(final long pTime) {
+ return roundToHour(pTime, TimeZone.getDefault());
+ }
+
+ /**
+ * Rounds the given time down to the closest hour, using the given timezone.
+ *
+ * @param pTime time
+ * @param pTimeZone the timezone to use when rounding
+ * @return the time rounded to the closest hour.
+ */
+ public static long roundToHour(final long pTime, final TimeZone pTimeZone) {
+ int offset = pTimeZone.getOffset(pTime);
+ return ((pTime / HOUR) * HOUR) - offset;
+ }
+
+ /**
+ * Rounds the given time down to the closest day, using the default timezone.
+ *
+ * @param pTime time
+ * @return the time rounded to the closest day.
+ */
+ public static long roundToDay(final long pTime) {
+ return roundToDay(pTime, TimeZone.getDefault());
+ }
+
+ /**
+ * Rounds the given time down to the closest day, using the given timezone.
+ *
+ * @param pTime time
+ * @param pTimeZone the timezone to use when rounding
+ * @return the time rounded to the closest day.
+ */
+ public static long roundToDay(final long pTime, final TimeZone pTimeZone) {
+ int offset = pTimeZone.getOffset(pTime);
+ return (((pTime + offset) / DAY) * DAY) - offset;
+ }
+}
diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/lang/Platform.java b/common/common-lang/src/main/java/com/twelvemonkeys/lang/Platform.java
index d69f37c3..8dadc5d2 100755
--- a/common/common-lang/src/main/java/com/twelvemonkeys/lang/Platform.java
+++ b/common/common-lang/src/main/java/com/twelvemonkeys/lang/Platform.java
@@ -4,26 +4,28 @@
*
* 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 "TwelveMonkeys" 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 OWNER 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.
+ * * 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.lang;
@@ -197,9 +199,10 @@ public final class Platform {
/**
* Enumeration of common System {@code Architecture}s.
- *
+ *
* For {@link #Unknown unknown architectures}, {@code toString()} will return
* the the same value as {@code System.getProperty("os.arch")}.
+ *
*
* @author Harald Kuhr
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/Platform.java#1 $
@@ -226,9 +229,10 @@ public final class Platform {
/**
* Enumeration of common {@code OperatingSystem}s.
- *
+ *
* For {@link #Unknown unknown operating systems}, {@code getName()} will return
* the the same value as {@code System.getProperty("os.name")}.
+ *
*
* @author Harald Kuhr
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/Platform.java#1 $
diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/lang/ReflectUtil.java b/common/common-lang/src/main/java/com/twelvemonkeys/lang/ReflectUtil.java
index 349be257..aefd92a0 100755
--- a/common/common-lang/src/main/java/com/twelvemonkeys/lang/ReflectUtil.java
+++ b/common/common-lang/src/main/java/com/twelvemonkeys/lang/ReflectUtil.java
@@ -1,137 +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 "TwelveMonkeys" 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 OWNER 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.lang;
-
-/**
- * Util class for various reflection-based operations.
- *
- * NOTE: This class is not considered part of the public API and may be
- * changed without notice
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/ReflectUtil.java#1 $
- */
-public final class ReflectUtil {
-
- /** Don't allow instances */
- private ReflectUtil() {}
-
- /**
- * Returns the primitive type for the given wrapper type.
- *
- * @param pType the wrapper type
- *
- * @return the primitive type
- *
- * @throws IllegalArgumentException if {@code pType} is not a primitive
- * wrapper
- */
- public static Class unwrapType(Class pType) {
- if (pType == Boolean.class) {
- return Boolean.TYPE;
- }
- else if (pType == Byte.class) {
- return Byte.TYPE;
- }
- else if (pType == Character.class) {
- return Character.TYPE;
- }
- else if (pType == Double.class) {
- return Double.TYPE;
- }
- else if (pType == Float.class) {
- return Float.TYPE;
- }
- else if (pType == Integer.class) {
- return Integer.TYPE;
- }
- else if (pType == Long.class) {
- return Long.TYPE;
- }
- else if (pType == Short.class) {
- return Short.TYPE;
- }
-
- throw new IllegalArgumentException("Not a primitive wrapper: " + pType);
- }
-
- /**
- * Returns the wrapper type for the given primitive type.
- *
- * @param pType the primitive tpye
- *
- * @return the wrapper type
- *
- * @throws IllegalArgumentException if {@code pType} is not a primitive
- * type
- */
- public static Class wrapType(Class pType) {
- if (pType == Boolean.TYPE) {
- return Boolean.class;
- }
- else if (pType == Byte.TYPE) {
- return Byte.class;
- }
- else if (pType == Character.TYPE) {
- return Character.class;
- }
- else if (pType == Double.TYPE) {
- return Double.class;
- }
- else if (pType == Float.TYPE) {
- return Float.class;
- }
- else if (pType == Integer.TYPE) {
- return Integer.class;
- }
- else if (pType == Long.TYPE) {
- return Long.class;
- }
- else if (pType == Short.TYPE) {
- return Short.class;
- }
-
- throw new IllegalArgumentException("Not a primitive type: " + pType);
- }
-
- /**
- * Returns {@code true} if the given type is a primitive wrapper.
- *
- * @param pType
- *
- * @return {@code true} if the given type is a primitive wrapper, otherwise
- * {@code false}
- */
- public static boolean isPrimitiveWrapper(Class pType) {
- return pType == Boolean.class || pType == Byte.class
- || pType == Character.class || pType == Double.class
- || pType == Float.class || pType == Integer.class
- || pType == Long.class || pType == Short.class;
- }
-}
+/*
+ * 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.lang;
+
+/**
+ * Util class for various reflection-based operations.
+ *
+ * NOTE: This class is not considered part of the public API and may be
+ * changed without notice
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/ReflectUtil.java#1 $
+ */
+public final class ReflectUtil {
+
+ /** Don't allow instances */
+ private ReflectUtil() {}
+
+ /**
+ * Returns the primitive type for the given wrapper type.
+ *
+ * @param pType the wrapper type
+ *
+ * @return the primitive type
+ *
+ * @throws IllegalArgumentException if {@code pType} is not a primitive
+ * wrapper
+ */
+ public static Class unwrapType(Class pType) {
+ if (pType == Boolean.class) {
+ return Boolean.TYPE;
+ }
+ else if (pType == Byte.class) {
+ return Byte.TYPE;
+ }
+ else if (pType == Character.class) {
+ return Character.TYPE;
+ }
+ else if (pType == Double.class) {
+ return Double.TYPE;
+ }
+ else if (pType == Float.class) {
+ return Float.TYPE;
+ }
+ else if (pType == Integer.class) {
+ return Integer.TYPE;
+ }
+ else if (pType == Long.class) {
+ return Long.TYPE;
+ }
+ else if (pType == Short.class) {
+ return Short.TYPE;
+ }
+
+ throw new IllegalArgumentException("Not a primitive wrapper: " + pType);
+ }
+
+ /**
+ * Returns the wrapper type for the given primitive type.
+ *
+ * @param pType the primitive tpye
+ *
+ * @return the wrapper type
+ *
+ * @throws IllegalArgumentException if {@code pType} is not a primitive
+ * type
+ */
+ public static Class wrapType(Class pType) {
+ if (pType == Boolean.TYPE) {
+ return Boolean.class;
+ }
+ else if (pType == Byte.TYPE) {
+ return Byte.class;
+ }
+ else if (pType == Character.TYPE) {
+ return Character.class;
+ }
+ else if (pType == Double.TYPE) {
+ return Double.class;
+ }
+ else if (pType == Float.TYPE) {
+ return Float.class;
+ }
+ else if (pType == Integer.TYPE) {
+ return Integer.class;
+ }
+ else if (pType == Long.TYPE) {
+ return Long.class;
+ }
+ else if (pType == Short.TYPE) {
+ return Short.class;
+ }
+
+ throw new IllegalArgumentException("Not a primitive type: " + pType);
+ }
+
+ /**
+ * Returns {@code true} if the given type is a primitive wrapper.
+ *
+ * @param pType
+ *
+ * @return {@code true} if the given type is a primitive wrapper, otherwise
+ * {@code false}
+ */
+ public static boolean isPrimitiveWrapper(Class pType) {
+ return pType == Boolean.class || pType == Byte.class
+ || pType == Character.class || pType == Double.class
+ || pType == Float.class || pType == Integer.class
+ || pType == Long.class || pType == Short.class;
+ }
+}
diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/lang/StringUtil.java b/common/common-lang/src/main/java/com/twelvemonkeys/lang/StringUtil.java
index 1d244b72..69979657 100755
--- a/common/common-lang/src/main/java/com/twelvemonkeys/lang/StringUtil.java
+++ b/common/common-lang/src/main/java/com/twelvemonkeys/lang/StringUtil.java
@@ -1,2150 +1,2161 @@
-/*
- * 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 "TwelveMonkeys" 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 OWNER 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.lang;
-
-import com.twelvemonkeys.util.StringTokenIterator;
-
-import java.awt.*;
-import java.lang.reflect.Array;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.sql.Timestamp;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.regex.PatternSyntaxException;
-import java.util.regex.Pattern;
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.UnsupportedCharsetException;
-
-/**
- * A utility class with some useful string manipulation methods.
- *
- * @author Harald Kuhr
- * @author Eirik Torske
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/StringUtil.java#2 $
- * @todo Consistency check: Method names, parameter sequence, Exceptions,
- * return values, null-value handling and parameter names (cosmetics).
- */
-public final class StringUtil {
-
- /**
- * The default delimiter string, used by the {@code toXXXArray()}
- * methods.
- * Its value is {@code ", \t\n\r\f"}.
- *
- *
- * @see #toStringArray(String)
- * @see #toIntArray(String)
- * @see #toLongArray(String)
- * @see #toDoubleArray(String)
- */
- public final static String DELIMITER_STRING = ", \t\n\r\f";
-
- // Avoid constructor showing up in API doc
- private StringUtil() {
- }
-
- /**
- * Constructs a new {@link String} by decoding the specified sub array of bytes using the specified charset.
- * Replacement for {@link String#String(byte[], int, int, String) new String(byte[], int, int, String)}, that does
- * not throw the checked {@link UnsupportedEncodingException},
- * but instead the unchecked {@link UnsupportedCharsetException} if the character set is not supported.
- *
- * @param pData the bytes to be decoded to characters
- * @param pOffset the index of the first byte to decode
- * @param pLength the number of bytes to decode
- * @param pCharset the name of a supported character set
- * @return a newly created string.
- * @throws UnsupportedCharsetException
- *
- * @see String#String(byte[], int, int, String)
- */
- public static String decode(final byte[] pData, final int pOffset, final int pLength, final String pCharset) {
- try {
- return new String(pData, pOffset, pLength, pCharset);
- }
- catch (UnsupportedEncodingException e) {
- throw new UnsupportedCharsetException(pCharset);
- }
- }
-
- /**
- * Returns the value of the given {@code Object}, as a {@code String}.
- * Unlike String.valueOf, this method returns {@code null}
- * instead of the {@code String} "null", if {@code null} is given as
- * the argument.
- *
- * @param pObj the Object to find the {@code String} value of.
- * @return the String value of the given object, or {@code null} if the
- * {@code pObj} == {@code null}.
- * @see String#valueOf(Object)
- * @see String#toString()
- */
- public static String valueOf(Object pObj) {
- return ((pObj != null) ? pObj.toString() : null);
- }
-
- /**
- * Converts a string to uppercase.
- *
- * @param pString the string to convert
- * @return the string converted to uppercase, or null if the argument was
- * null.
- */
- public static String toUpperCase(String pString) {
- if (pString != null) {
- return pString.toUpperCase();
- }
- return null;
- }
-
- /**
- * Converts a string to lowercase.
- *
- * @param pString the string to convert
- * @return the string converted to lowercase, or null if the argument was
- * null.
- */
- public static String toLowerCase(String pString) {
- if (pString != null) {
- return pString.toLowerCase();
- }
- return null;
- }
-
- /**
- * Tests if a String is null, or contains nothing but white-space.
- *
- * @param pString The string to test
- * @return true if the string is null or contains only whitespace,
- * otherwise false.
- */
- public static boolean isEmpty(String pString) {
- return ((pString == null) || (pString.trim().length() == 0));
- }
-
- /**
- * Tests a string array, to see if all items are null or an empty string.
- *
- * @param pStringArray The string array to check.
- * @return true if the string array is null or only contains string items
- * that are null or contain only whitespace, otherwise false.
- */
- public static boolean isEmpty(String[] pStringArray) {
- // No elements to test
- if (pStringArray == null) {
- return true;
- }
-
- // Test all the elements
- for (String string : pStringArray) {
- if (!isEmpty(string)) {
- return false;
- }
- }
-
- // All elements are empty
- return true;
- }
-
- /**
- * Tests if a string contains another string.
- *
- * @param pContainer The string to test
- * @param pLookFor The string to look for
- * @return {@code true} if the container string is contains the string, and
- * both parameters are non-{@code null}, otherwise {@code false}.
- */
- public static boolean contains(String pContainer, String pLookFor) {
- return ((pContainer != null) && (pLookFor != null) && (pContainer.indexOf(pLookFor) >= 0));
- }
-
- /**
- * Tests if a string contains another string, ignoring case.
- *
- * @param pContainer The string to test
- * @param pLookFor The string to look for
- * @return {@code true} if the container string is contains the string, and
- * both parameters are non-{@code null}, otherwise {@code false}.
- * @see #contains(String,String)
- */
- public static boolean containsIgnoreCase(String pContainer, String pLookFor) {
- return indexOfIgnoreCase(pContainer, pLookFor, 0) >= 0;
- }
-
- /**
- * Tests if a string contains a specific character.
- *
- * @param pString The string to check.
- * @param pChar The character to search for.
- * @return true if the string contains the specific character.
- */
- public static boolean contains(final String pString, final int pChar) {
- return ((pString != null) && (pString.indexOf(pChar) >= 0));
- }
-
- /**
- * Tests if a string contains a specific character, ignoring case.
- *
- * @param pString The string to check.
- * @param pChar The character to search for.
- * @return true if the string contains the specific character.
- */
- public static boolean containsIgnoreCase(String pString, int pChar) {
- return ((pString != null)
- && ((pString.indexOf(Character.toLowerCase((char) pChar)) >= 0)
- || (pString.indexOf(Character.toUpperCase((char) pChar)) >= 0)));
-
- // NOTE: I don't convert the string to uppercase, but instead test
- // the string (potentially) two times, as this is more efficient for
- // long strings (in most cases).
- }
-
- /**
- * Returns the index within this string of the first occurrence of the
- * specified substring.
- *
- * @param pString The string to test
- * @param pLookFor The string to look for
- * @return if the string argument occurs as a substring within this object,
- * then the index of the first character of the first such substring is
- * returned; if it does not occur as a substring, -1 is returned.
- * @see String#indexOf(String)
- */
- public static int indexOfIgnoreCase(String pString, String pLookFor) {
- return indexOfIgnoreCase(pString, pLookFor, 0);
- }
-
- /**
- * Returns the index within this string of the first occurrence of the
- * specified substring, starting at the specified index.
- *
- * @param pString The string to test
- * @param pLookFor The string to look for
- * @param pPos The first index to test
- * @return if the string argument occurs as a substring within this object,
- * then the index of the first character of the first such substring is
- * returned; if it does not occur as a substring, -1 is returned.
- * @see String#indexOf(String,int)
- */
- public static int indexOfIgnoreCase(String pString, String pLookFor, int pPos) {
- if ((pString == null) || (pLookFor == null)) {
- return -1;
- }
- if (pLookFor.length() == 0) {
- return pPos;// All strings "contains" the empty string
- }
- if (pLookFor.length() > pString.length()) {
- return -1;// Cannot contain string longer than itself
- }
-
- // Get first char
- char firstL = Character.toLowerCase(pLookFor.charAt(0));
- char firstU = Character.toUpperCase(pLookFor.charAt(0));
- int indexLower = 0;
- int indexUpper = 0;
-
- for (int i = pPos; i <= (pString.length() - pLookFor.length()); i++) {
-
- // Peek for first char
- indexLower = ((indexLower >= 0) && (indexLower <= i))
- ? pString.indexOf(firstL, i)
- : indexLower;
- indexUpper = ((indexUpper >= 0) && (indexUpper <= i))
- ? pString.indexOf(firstU, i)
- : indexUpper;
- if (indexLower < 0) {
- if (indexUpper < 0) {
- return -1;// First char not found
- }
- else {
- i = indexUpper;// Only upper
- }
- }
- else if (indexUpper < 0) {
- i = indexLower;// Only lower
- }
- else {
-
- // Both found, select first occurence
- i = (indexLower < indexUpper)
- ? indexLower
- : indexUpper;
- }
-
- // Only one?
- if (pLookFor.length() == 1) {
- return i;// The only char found!
- }
-
- // Test if we still have enough chars
- else if (i > (pString.length() - pLookFor.length())) {
- return -1;
- }
-
- // Test if last char equals! (regionMatches is expensive)
- else if ((pString.charAt(i + pLookFor.length() - 1) != Character.toLowerCase(pLookFor.charAt(pLookFor.length() - 1)))
- && (pString.charAt(i + pLookFor.length() - 1) != Character.toUpperCase(pLookFor.charAt(pLookFor.length() - 1)))) {
- continue;// Nope, try next
- }
-
- // Test from second char, until second-last char
- else if ((pLookFor.length() <= 2) || pString.regionMatches(true, i + 1, pLookFor, 1, pLookFor.length() - 2)) {
- return i;
- }
- }
- return -1;
- }
-
- /**
- * Returns the index within this string of the rightmost occurrence of the
- * specified substring. The rightmost empty string "" is considered to
- * occur at the index value {@code pString.length() - 1}.
- *
- * @param pString The string to test
- * @param pLookFor The string to look for
- * @return If the string argument occurs one or more times as a substring
- * within this object at a starting index no greater than fromIndex, then
- * the index of the first character of the last such substring is returned.
- * If it does not occur as a substring starting at fromIndex or earlier, -1
- * is returned.
- * @see String#lastIndexOf(String)
- */
- public static int lastIndexOfIgnoreCase(String pString, String pLookFor) {
- return lastIndexOfIgnoreCase(pString, pLookFor, pString != null ? pString.length() - 1 : -1);
- }
-
- /**
- * Returns the index within this string of the rightmost occurrence of the
- * specified substring. The rightmost empty string "" is considered to
- * occur at the index value {@code pPos}
- *
- * @param pString The string to test
- * @param pLookFor The string to look for
- * @param pPos The last index to test
- * @return If the string argument occurs one or more times as a substring
- * within this object at a starting index no greater than fromIndex, then
- * the index of the first character of the last such substring is returned.
- * If it does not occur as a substring starting at fromIndex or earlier, -1
- * is returned.
- * @see String#lastIndexOf(String,int)
- */
- public static int lastIndexOfIgnoreCase(String pString, String pLookFor, int pPos) {
- if ((pString == null) || (pLookFor == null)) {
- return -1;
- }
- if (pLookFor.length() == 0) {
- return pPos;// All strings "contains" the empty string
- }
- if (pLookFor.length() > pString.length()) {
- return -1;// Cannot contain string longer than itself
- }
-
- // Get first char
- char firstL = Character.toLowerCase(pLookFor.charAt(0));
- char firstU = Character.toUpperCase(pLookFor.charAt(0));
- int indexLower = pPos;
- int indexUpper = pPos;
-
- for (int i = pPos; i >= 0; i--) {
-
- // Peek for first char
- indexLower = ((indexLower >= 0) && (indexLower >= i))
- ? pString.lastIndexOf(firstL, i)
- : indexLower;
- indexUpper = ((indexUpper >= 0) && (indexUpper >= i))
- ? pString.lastIndexOf(firstU, i)
- : indexUpper;
- if (indexLower < 0) {
- if (indexUpper < 0) {
- return -1;// First char not found
- }
- else {
- i = indexUpper;// Only upper
- }
- }
- else if (indexUpper < 0) {
- i = indexLower;// Only lower
- }
- else {
-
- // Both found, select last occurence
- i = (indexLower > indexUpper)
- ? indexLower
- : indexUpper;
- }
-
- // Only one?
- if (pLookFor.length() == 1) {
- return i;// The only char found!
- }
-
- // Test if we still have enough chars
- else if (i > (pString.length() - pLookFor.length())) {
- //return -1;
- continue;
- }
-
- // Test if last char equals! (regionMatches is expensive)
- else
- if ((pString.charAt(i + pLookFor.length() - 1) != Character.toLowerCase(pLookFor.charAt(pLookFor.length() - 1)))
- && (pString.charAt(i + pLookFor.length() - 1) != Character.toUpperCase(pLookFor.charAt(pLookFor.length() - 1)))) {
- continue;// Nope, try next
- }
-
- // Test from second char, until second-last char
- else
- if ((pLookFor.length() <= 2) || pString.regionMatches(true, i + 1, pLookFor, 1, pLookFor.length() - 2)) {
- return i;
- }
- }
- return -1;
- }
-
- /**
- * Returns the index within this string of the first occurrence of the
- * specified character.
- *
- * @param pString The string to test
- * @param pChar The character to look for
- * @return if the string argument occurs as a substring within this object,
- * then the index of the first character of the first such substring is
- * returned; if it does not occur as a substring, -1 is returned.
- * @see String#indexOf(int)
- */
- public static int indexOfIgnoreCase(String pString, int pChar) {
- return indexOfIgnoreCase(pString, pChar, 0);
- }
-
- /**
- * Returns the index within this string of the first occurrence of the
- * specified character, starting at the specified index.
- *
- * @param pString The string to test
- * @param pChar The character to look for
- * @param pPos The first index to test
- * @return if the string argument occurs as a substring within this object,
- * then the index of the first character of the first such substring is
- * returned; if it does not occur as a substring, -1 is returned.
- * @see String#indexOf(int,int)
- */
- public static int indexOfIgnoreCase(String pString, int pChar, int pPos) {
- if ((pString == null)) {
- return -1;
- }
-
- // Get first char
- char lower = Character.toLowerCase((char) pChar);
- char upper = Character.toUpperCase((char) pChar);
- int indexLower;
- int indexUpper;
-
- // Test for char
- indexLower = pString.indexOf(lower, pPos);
- indexUpper = pString.indexOf(upper, pPos);
- if (indexLower < 0) {
-
- /* if (indexUpper < 0)
- return -1; // First char not found
- else */
- return indexUpper;// Only upper
- }
- else if (indexUpper < 0) {
- return indexLower;// Only lower
- }
- else {
-
- // Both found, select first occurence
- return (indexLower < indexUpper)
- ? indexLower
- : indexUpper;
- }
- }
-
- /**
- * Returns the index within this string of the last occurrence of the
- * specified character.
- *
- * @param pString The string to test
- * @param pChar The character to look for
- * @return if the string argument occurs as a substring within this object,
- * then the index of the first character of the first such substring is
- * returned; if it does not occur as a substring, -1 is returned.
- * @see String#lastIndexOf(int)
- */
- public static int lastIndexOfIgnoreCase(String pString, int pChar) {
- return lastIndexOfIgnoreCase(pString, pChar, pString != null ? pString.length() : -1);
- }
-
- /**
- * Returns the index within this string of the last occurrence of the
- * specified character, searching backward starting at the specified index.
- *
- * @param pString The string to test
- * @param pChar The character to look for
- * @param pPos The last index to test
- * @return if the string argument occurs as a substring within this object,
- * then the index of the first character of the first such substring is
- * returned; if it does not occur as a substring, -1 is returned.
- * @see String#lastIndexOf(int,int)
- */
- public static int lastIndexOfIgnoreCase(String pString, int pChar, int pPos) {
- if ((pString == null)) {
- return -1;
- }
-
- // Get first char
- char lower = Character.toLowerCase((char) pChar);
- char upper = Character.toUpperCase((char) pChar);
- int indexLower;
- int indexUpper;
-
- // Test for char
- indexLower = pString.lastIndexOf(lower, pPos);
- indexUpper = pString.lastIndexOf(upper, pPos);
- if (indexLower < 0) {
-
- /* if (indexUpper < 0)
- return -1; // First char not found
- else */
- return indexUpper;// Only upper
- }
- else if (indexUpper < 0) {
- return indexLower;// Only lower
- }
- else {
-
- // Both found, select last occurence
- return (indexLower > indexUpper)
- ? indexLower
- : indexUpper;
- }
- }
-
- /**
- * Trims the argument string for whitespace on the left side only.
- *
- * @param pString the string to trim
- * @return the string with no whitespace on the left, or {@code null} if
- * the string argument is {@code null}.
- * @see #rtrim
- * @see String#trim()
- */
- public static String ltrim(String pString) {
- if ((pString == null) || (pString.length() == 0)) {
- return pString;// Null or empty string
- }
- for (int i = 0; i < pString.length(); i++) {
- if (!Character.isWhitespace(pString.charAt(i))) {
- if (i == 0) {
- return pString;// First char is not whitespace
- }
- else {
- return pString.substring(i);// Return rest after whitespace
- }
- }
- }
-
- // If all whitespace, return empty string
- return "";
- }
-
- /**
- * Trims the argument string for whitespace on the right side only.
- *
- * @param pString the string to trim
- * @return the string with no whitespace on the right, or {@code null} if
- * the string argument is {@code null}.
- * @see #ltrim
- * @see String#trim()
- */
- public static String rtrim(String pString) {
- if ((pString == null) || (pString.length() == 0)) {
- return pString;// Null or empty string
- }
- for (int i = pString.length(); i > 0; i--) {
- if (!Character.isWhitespace(pString.charAt(i - 1))) {
- if (i == pString.length()) {
- return pString;// First char is not whitespace
- }
- else {
- return pString.substring(0, i);// Return before whitespace
- }
- }
- }
-
- // If all whitespace, return empty string
- return "";
- }
-
- /**
- * Replaces a substring of a string with another string. All matches are
- * replaced.
- *
- * @param pSource The source String
- * @param pPattern The pattern to replace
- * @param pReplace The new String to be inserted instead of the
- * replace String
- * @return The new String with the pattern replaced
- */
- public static String replace(String pSource, String pPattern, String pReplace) {
- if (pPattern.length() == 0) {
- return pSource;// Special case: No pattern to replace
- }
-
- int match;
- int offset = 0;
- StringBuilder result = new StringBuilder();
-
- // Loop string, until last occurence of pattern, and replace
- while ((match = pSource.indexOf(pPattern, offset)) != -1) {
- // Append everything until pattern
- result.append(pSource.substring(offset, match));
- // Append the replace string
- result.append(pReplace);
- offset = match + pPattern.length();
- }
-
- // Append rest of string and return
- result.append(pSource.substring(offset));
-
- return result.toString();
- }
-
- /**
- * Replaces a substring of a string with another string, ignoring case.
- * All matches are replaced.
- *
- * @param pSource The source String
- * @param pPattern The pattern to replace
- * @param pReplace The new String to be inserted instead of the
- * replace String
- * @return The new String with the pattern replaced
- * @see #replace(String,String,String)
- */
- public static String replaceIgnoreCase(String pSource, String pPattern, String pReplace) {
- if (pPattern.length() == 0) {
- return pSource;// Special case: No pattern to replace
- }
- int match;
- int offset = 0;
- StringBuilder result = new StringBuilder();
-
- while ((match = indexOfIgnoreCase(pSource, pPattern, offset)) != -1) {
- result.append(pSource.substring(offset, match));
- result.append(pReplace);
- offset = match + pPattern.length();
- }
- result.append(pSource.substring(offset));
- return result.toString();
- }
-
- /**
- * Cuts a string between two words, before a sepcified length, if the
- * string is longer than the maxium lenght. The string is optionally padded
- * with the pad argument. The method assumes words to be separated by the
- * space character (" ").
- * Note that the maximum length argument is absolute, and will also include
- * the length of the padding.
- *
- * @param pString The string to cut
- * @param pMaxLen The maximum length before cutting
- * @param pPad The string to append at the end, aftrer cutting
- * @return The cutted string with padding, or the original string, if it
- * was shorter than the max length.
- * @see #pad(String,int,String,boolean)
- */
- public static String cut(String pString, int pMaxLen, String pPad) {
- if (pString == null) {
- return null;
- }
- if (pPad == null) {
- pPad = "";
- }
- int len = pString.length();
-
- if (len > pMaxLen) {
- len = pString.lastIndexOf(' ', pMaxLen - pPad.length());
- }
- else {
- return pString;
- }
- return pString.substring(0, len) + pPad;
- }
-
- /**
- * Makes the Nth letter of a String uppercase. If the index is outside the
- * the length of the argument string, the argument is simply returned.
- *
- * @param pString The string to capitalize
- * @param pIndex The base-0 index of the char to capitalize.
- * @return The capitalized string, or null, if a null argument was given.
- */
- public static String capitalize(String pString, int pIndex) {
- if (pIndex < 0) {
- throw new IndexOutOfBoundsException("Negative index not allowed: " + pIndex);
- }
- if (pString == null || pString.length() <= pIndex) {
- return pString;
- }
-
- // This is the fastest method, according to my tests
-
- // Skip array duplication if allready capitalized
- if (Character.isUpperCase(pString.charAt(pIndex))) {
- return pString;
- }
-
- // Convert to char array, capitalize and create new String
- char[] charArray = pString.toCharArray();
- charArray[pIndex] = Character.toUpperCase(charArray[pIndex]);
- return new String(charArray);
-
- /**
- StringBuilder buf = new StringBuilder(pString);
- buf.setCharAt(pIndex, Character.toUpperCase(buf.charAt(pIndex)));
- return buf.toString();
- //*/
-
- /**
- return pString.substring(0, pIndex)
- + Character.toUpperCase(pString.charAt(pIndex))
- + pString.substring(pIndex + 1);
- //*/
- }
-
- /**
- * Makes the first letter of a String uppercase.
- *
- * @param pString The string to capitalize
- * @return The capitalized string, or null, if a null argument was given.
- */
- public static String capitalize(String pString) {
- return capitalize(pString, 0);
- }
-
- /**
- * Formats a number with leading zeroes, to a specified length.
- *
- * @param pNum The number to format
- * @param pLen The number of digits
- * @return A string containing the formatted number
- * @throws IllegalArgumentException Thrown, if the number contains
- * more digits than allowed by the length argument.
- * @see #pad(String,int,String,boolean)
- * @deprecated Use StringUtil.pad instead!
- */
-
- /*public*/
- static String formatNumber(long pNum, int pLen) throws IllegalArgumentException {
- StringBuilder result = new StringBuilder();
-
- if (pNum >= Math.pow(10, pLen)) {
- throw new IllegalArgumentException("The number to format cannot contain more digits than the length argument specifies!");
- }
- for (int i = pLen; i > 1; i--) {
- if (pNum < Math.pow(10, i - 1)) {
- result.append('0');
- }
- else {
- break;
- }
- }
- result.append(pNum);
- return result.toString();
- }
-
- /**
- * String length check with simple concatenation of selected pad-string.
- * E.g. a zip number from 123 to the correct 0123.
- *
- * @param pSource The source string.
- * @param pRequiredLength The accurate length of the resulting string.
- * @param pPadString The string for concatenation.
- * @param pPrepend The location of fill-ins, prepend (true),
- * or append (false)
- * @return a concatenated string.
- * @todo What if source is allready longer than required length?
- * @todo Consistency with cut
- * @see #cut(String,int,String)
- */
- public static String pad(String pSource, int pRequiredLength, String pPadString, boolean pPrepend) {
- if (pPadString == null || pPadString.length() == 0) {
- throw new IllegalArgumentException("Pad string: \"" + pPadString + "\"");
- }
-
- if (pSource.length() >= pRequiredLength) {
- return pSource;
- }
-
- // TODO: Benchmark the new version against the old one, to see if it's really faster
- // Rewrite to first create pad
- // - pad += pad; - until length is >= gap
- // then append the pad and cut if too long
- int gap = pRequiredLength - pSource.length();
- StringBuilder result = new StringBuilder(pPadString);
- while (result.length() < gap) {
- result.append(result);
- }
-
- if (result.length() > gap) {
- result.delete(gap, result.length());
- }
-
- return pPrepend ? result.append(pSource).toString() : result.insert(0, pSource).toString();
-
- /*
- StringBuilder result = new StringBuilder(pSource);
-
- // Concatenation until proper string length
- while (result.length() < pRequiredLength) {
- // Prepend or append
- if (pPrepend) { // Front
- result.insert(0, pPadString);
- }
- else { // Back
- result.append(pPadString);
- }
- }
-
- // Truncate
- if (result.length() > pRequiredLength) {
- if (pPrepend) {
- result.delete(0, result.length() - pRequiredLength);
- }
- else {
- result.delete(pRequiredLength, result.length());
- }
- }
- return result.toString();
- */
- }
-
- /**
- * Converts the string to a date, using the default date format.
- *
- * @param pString the string to convert
- * @return the date
- * @see DateFormat
- * @see DateFormat#getInstance()
- */
- public static Date toDate(String pString) {
- // Default
- return toDate(pString, DateFormat.getInstance());
- }
-
- /**
- * Converts the string to a date, using the given format.
- *
- * @param pString the string to convert
- * @param pFormat the date format
- * @return the date
- * @todo cache formats?
- * @see java.text.SimpleDateFormat
- * @see java.text.SimpleDateFormat#SimpleDateFormat(String)
- */
- public static Date toDate(String pString, String pFormat) {
- // Get the format from cache, or create new and insert
- // Return new date
- return toDate(pString, new SimpleDateFormat(pFormat));
- }
-
- /**
- * Converts the string to a date, using the given format.
- *
- * @param pString the string to convert
- * @param pFormat the date format
- * @return the date
- * @see SimpleDateFormat
- * @see SimpleDateFormat#SimpleDateFormat(String)
- * @see DateFormat
- */
- public static Date toDate(final String pString, final DateFormat pFormat) {
- try {
- synchronized (pFormat) {
- // Parse date using given format
- return pFormat.parse(pString);
- }
- }
- catch (ParseException pe) {
- // Wrap in RuntimeException
- throw new IllegalArgumentException(pe.getMessage());
- }
- }
-
- /**
- * Converts the string to a jdbc Timestamp, using the standard Timestamp
- * escape format.
- *
- * @param pValue the value
- * @return a new {@code Timestamp}
- * @see java.sql.Timestamp
- * @see java.sql.Timestamp#valueOf(String)
- */
- public static Timestamp toTimestamp(final String pValue) {
- // Parse date using default format
- return Timestamp.valueOf(pValue);
- }
-
- /**
- * Converts a delimiter separated String to an array of Strings.
- *
- * @param pString The comma-separated string
- * @param pDelimiters The delimiter string
- * @return a {@code String} array containing the delimiter separated elements
- */
- public static String[] toStringArray(String pString, String pDelimiters) {
- if (isEmpty(pString)) {
- return new String[0];
- }
-
- StringTokenIterator st = new StringTokenIterator(pString, pDelimiters);
- List v = new ArrayList();
-
- while (st.hasMoreElements()) {
- v.add(st.nextToken());
- }
-
- return v.toArray(new String[v.size()]);
- }
-
- /**
- * Converts a comma-separated String to an array of Strings.
- *
- * @param pString The comma-separated string
- * @return a {@code String} array containing the comma-separated elements
- * @see #toStringArray(String,String)
- */
- public static String[] toStringArray(String pString) {
- return toStringArray(pString, DELIMITER_STRING);
- }
-
- /**
- * Converts a comma-separated String to an array of ints.
- *
- * @param pString The comma-separated string
- * @param pDelimiters The delimiter string
- * @param pBase The radix
- * @return an {@code int} array
- * @throws NumberFormatException if any of the elements are not parseable
- * as an int
- */
- public static int[] toIntArray(String pString, String pDelimiters, int pBase) {
- if (isEmpty(pString)) {
- return new int[0];
- }
-
- // Some room for improvement here...
- String[] temp = toStringArray(pString, pDelimiters);
- int[] array = new int[temp.length];
-
- for (int i = 0; i < array.length; i++) {
- array[i] = Integer.parseInt(temp[i], pBase);
- }
- return array;
- }
-
- /**
- * Converts a comma-separated String to an array of ints.
- *
- * @param pString The comma-separated string
- * @return an {@code int} array
- * @throws NumberFormatException if any of the elements are not parseable
- * as an int
- * @see #toStringArray(String,String)
- * @see #DELIMITER_STRING
- */
- public static int[] toIntArray(String pString) {
- return toIntArray(pString, DELIMITER_STRING, 10);
- }
-
- /**
- * Converts a comma-separated String to an array of ints.
- *
- * @param pString The comma-separated string
- * @param pDelimiters The delimiter string
- * @return an {@code int} array
- * @throws NumberFormatException if any of the elements are not parseable
- * as an int
- * @see #toIntArray(String,String)
- */
- public static int[] toIntArray(String pString, String pDelimiters) {
- return toIntArray(pString, pDelimiters, 10);
- }
-
- /**
- * Converts a comma-separated String to an array of longs.
- *
- * @param pString The comma-separated string
- * @param pDelimiters The delimiter string
- * @return a {@code long} array
- * @throws NumberFormatException if any of the elements are not parseable
- * as a long
- */
- public static long[] toLongArray(String pString, String pDelimiters) {
- if (isEmpty(pString)) {
- return new long[0];
- }
-
- // Some room for improvement here...
- String[] temp = toStringArray(pString, pDelimiters);
- long[] array = new long[temp.length];
-
- for (int i = 0; i < array.length; i++) {
- array[i] = Long.parseLong(temp[i]);
- }
- return array;
- }
-
- /**
- * Converts a comma-separated String to an array of longs.
- *
- * @param pString The comma-separated string
- * @return a {@code long} array
- * @throws NumberFormatException if any of the elements are not parseable
- * as a long
- * @see #toStringArray(String,String)
- * @see #DELIMITER_STRING
- */
- public static long[] toLongArray(String pString) {
- return toLongArray(pString, DELIMITER_STRING);
- }
-
- /**
- * Converts a comma-separated String to an array of doubles.
- *
- * @param pString The comma-separated string
- * @param pDelimiters The delimiter string
- * @return a {@code double} array
- * @throws NumberFormatException if any of the elements are not parseable
- * as a double
- */
- public static double[] toDoubleArray(String pString, String pDelimiters) {
- if (isEmpty(pString)) {
- return new double[0];
- }
-
- // Some room for improvement here...
- String[] temp = toStringArray(pString, pDelimiters);
- double[] array = new double[temp.length];
-
- for (int i = 0; i < array.length; i++) {
- array[i] = Double.valueOf(temp[i]);
-
- // Double.parseDouble() is 1.2...
- }
- return array;
- }
-
- /**
- * Converts a comma-separated String to an array of doubles.
- *
- * @param pString The comma-separated string
- * @return a {@code double} array
- * @throws NumberFormatException if any of the elements are not parseable
- * as a double
- * @see #toDoubleArray(String,String)
- * @see #DELIMITER_STRING
- */
- public static double[] toDoubleArray(String pString) {
- return toDoubleArray(pString, DELIMITER_STRING);
- }
-
- /**
- * Parses a string to a Color.
- * The argument can be a color constant (static constant from
- * {@link java.awt.Color java.awt.Color}), like {@code black} or
- * {@code red}, or it can be HTML/CSS-style, on the format:
- *
- * - {@code #RRGGBB}, where RR, GG and BB means two digit
- * hexadecimal for red, green and blue values respectively.
- * - {@code #AARRGGBB}, as above, with AA as alpha component.
- * - {@code #RGB}, where R, G and B means one digit
- * hexadecimal for red, green and blue values respectively.
- * - {@code #ARGB}, as above, with A as alpha component.
- *
- *
- * @param pString the string representation of the color
- * @return the {@code Color} object, or {@code null} if the argument
- * is {@code null}
- * @throws IllegalArgumentException if the string does not map to a color.
- * @see java.awt.Color
- */
- public static Color toColor(String pString) {
- // No string, no color
- if (pString == null) {
- return null;
- }
-
- // #RRGGBB format
- if (pString.charAt(0) == '#') {
- int r = 0;
- int g = 0;
- int b = 0;
- int a = -1;// alpha
-
- if (pString.length() >= 7) {
- int idx = 1;
-
- // AA
- if (pString.length() >= 9) {
- a = Integer.parseInt(pString.substring(idx, idx + 2), 0x10);
- idx += 2;
- }
- // RR GG BB
- r = Integer.parseInt(pString.substring(idx, idx + 2), 0x10);
- g = Integer.parseInt(pString.substring(idx + 2, idx + 4), 0x10);
- b = Integer.parseInt(pString.substring(idx + 4, idx + 6), 0x10);
-
- }
- else if (pString.length() >= 4) {
- int idx = 1;
-
- // A
- if (pString.length() >= 5) {
- a = Integer.parseInt(pString.substring(idx, ++idx), 0x10) * 0x10;
- }
- // R G B
- r = Integer.parseInt(pString.substring(idx, ++idx), 0x10) * 0x10;
- g = Integer.parseInt(pString.substring(idx, ++idx), 0x10) * 0x10;
- b = Integer.parseInt(pString.substring(idx, ++idx), 0x10) * 0x10;
- }
- if (a != -1) {
- // With alpha
- return new Color(r, g, b, a);
- }
-
- // No alpha
- return new Color(r, g, b);
- }
-
- // Get color by name
- try {
- Class colorClass = Color.class;
- Field field = null;
-
- // Workaround for stupidity in Color class constant field names
- try {
- field = colorClass.getField(pString);
- }
- catch (Exception e) {
- // Don't care, this is just a workaround...
- }
- if (field == null) {
- // NOTE: The toLowerCase() on the next line will lose darkGray
- // and lightGray...
- field = colorClass.getField(pString.toLowerCase());
- }
-
- // Only try to get public final fields
- int mod = field.getModifiers();
-
- if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) {
- return (Color) field.get(null);
- }
- }
- catch (NoSuchFieldException nsfe) {
- // No such color, throw illegal argument?
- throw new IllegalArgumentException("No such color: " + pString);
- }
- catch (SecurityException se) {
- // Can't access field, return null
- }
- catch (IllegalAccessException iae) {
- // Can't happen, as the field must be public static
- }
- catch (IllegalArgumentException iar) {
- // Can't happen, as the field must be static
- }
-
- // This should never be reached, but you never know... ;-)
- return null;
- }
-
- /**
- * Creates a HTML/CSS String representation of the given color.
- * The HTML/CSS color format is defined as:
- *
- * - {@code #RRGGBB}, where RR, GG and BB means two digit
- * hexadecimal for red, green and blue values respectively.
- * - {@code #AARRGGBB}, as above, with AA as alpha component.
- *
- *
- * Examlples: {@code toColorString(Color.red) == "#ff0000"},
- * {@code toColorString(new Color(0xcc, 0xcc, 0xcc)) == "#cccccc"}.
- *
- * @param pColor the color
- * @return A String representation of the color on HTML/CSS form
- * @todo Consider moving to ImageUtil?
- */
- public static String toColorString(Color pColor) {
- // Not a color...
- if (pColor == null) {
- return null;
- }
-
- StringBuilder str = new StringBuilder(Integer.toHexString(pColor.getRGB()));
-
- // Make sure string is 8 chars
- for (int i = str.length(); i < 8; i++) {
- str.insert(0, '0');
- }
-
- // All opaque is default
- if (str.charAt(0) == 'f' && str.charAt(1) == 'f') {
- str.delete(0, 2);
- }
-
- // Prepend hash
- return str.insert(0, '#').toString();
- }
-
- /**
- * Tests a string, to see if it is an number (element of Z).
- * Valid integers are positive natural numbers (1, 2, 3, ...),
- * their negatives (?1, ?2, ?3, ...) and the number zero.
- *
- * Note that there is no guarantees made, that this number can be
- * represented as either an int or a long.
- *
- * @param pString The string to check.
- * @return true if the String is a natural number.
- */
- public static boolean isNumber(String pString) {
- if (isEmpty(pString)) {
- return false;
- }
-
- // Special case for first char, may be minus sign ('-')
- char ch = pString.charAt(0);
- if (!(ch == '-' || Character.isDigit(ch))) {
- return false;
- }
-
- // Test every char
- for (int i = 1; i < pString.length(); i++) {
- if (!Character.isDigit(pString.charAt(i))) {
- return false;
- }
- }
-
- // All digits must be a natural number
- return true;
- }
-
- /*
- * This version is benchmarked against toStringArray and found to be
- * increasingly slower, the more elements the string contains.
- * Kept here
- */
-
- /**
- * Removes all occurences of a specific character in a string.
- * This method is not design for efficiency!
- *
- *
- * @param pSource
- * @param pSubstring
- * @param pPosition
- * @return the modified string.
- */
-
- /*
- public static String removeChar(String pSourceString, final char pBadChar) {
-
- char[] sourceCharArray = pSourceString.toCharArray();
- List modifiedCharList = new Vector(sourceCharArray.length, 1);
-
- // Filter the string
- for (int i = 0; i < sourceCharArray.length; i++) {
- if (sourceCharArray[i] != pBadChar) {
- modifiedCharList.add(new Character(sourceCharArray[i]));
- }
- }
-
- // Clean the character list
- modifiedCharList = (List) CollectionUtil.purifyCollection((Collection) modifiedCharList);
-
- // Create new modified String
- char[] modifiedCharArray = new char[modifiedCharList.size()];
- for (int i = 0; i < modifiedCharArray.length; i++) {
- modifiedCharArray[i] = ((Character) modifiedCharList.get(i)).charValue();
- }
-
- return new String(modifiedCharArray);
- }
- */
-
- /**
- *
- * This method is not design for efficiency!
- *
- * @param pSourceString The String for modification.
- * @param pBadChars The char array containing the characters to remove from the source string.
- * @return the modified string.
- * @-deprecated Not tested yet!
- *
- */
-
- /*
- public static String removeChars(String pSourceString, final char[] pBadChars) {
-
- char[] sourceCharArray = pSourceString.toCharArray();
- List modifiedCharList = new Vector(sourceCharArray.length, 1);
-
- Map badCharMap = new Hashtable();
- Character dummyChar = new Character('*');
- for (int i = 0; i < pBadChars.length; i++) {
- badCharMap.put(new Character(pBadChars[i]), dummyChar);
- }
-
- // Filter the string
- for (int i = 0; i < sourceCharArray.length; i++) {
- Character arrayChar = new Character(sourceCharArray[i]);
- if (!badCharMap.containsKey(arrayChar)) {
- modifiedCharList.add(new Character(sourceCharArray[i]));
- }
- }
-
- // Clean the character list
- modifiedCharList = (List) CollectionUtil.purifyCollection((Collection) modifiedCharList);
-
- // Create new modified String
- char[] modifiedCharArray = new char[modifiedCharList.size()];
- for (int i = 0; i < modifiedCharArray.length; i++) {
- modifiedCharArray[i] = ((Character) modifiedCharList.get(i)).charValue();
- }
-
- return new String(modifiedCharArray);
-
- }
- */
-
- /**
- * Ensures that a string includes a given substring at a given position.
- *
- * Extends the string with a given string if it is not already there.
- * E.g an URL "www.vg.no", to "http://www.vg.no".
- *
- * @param pSource The source string.
- * @param pSubstring The substring to include.
- * @param pPosition The location of the fill-in, the index starts with 0.
- * @return the string, with the substring at the given location.
- */
- static String ensureIncludesAt(String pSource, String pSubstring, int pPosition) {
- StringBuilder newString = new StringBuilder(pSource);
-
- try {
- String existingSubstring = pSource.substring(pPosition, pPosition + pSubstring.length());
-
- if (!existingSubstring.equalsIgnoreCase(pSubstring)) {
- newString.insert(pPosition, pSubstring);
- }
- }
- catch (Exception e) {
- // Do something!?
- }
- return newString.toString();
- }
-
- /**
- * Ensures that a string does not include a given substring at a given
- * position.
- *
- * Removes a given substring from a string if it is there.
- * E.g an URL "http://www.vg.no", to "www.vg.no".
- *
- * @param pSource The source string.
- * @param pSubstring The substring to check and possibly remove.
- * @param pPosition The location of possible substring removal, the index starts with 0.
- * @return the string, without the substring at the given location.
- */
- static String ensureExcludesAt(String pSource, String pSubstring, int pPosition) {
- StringBuilder newString = new StringBuilder(pSource);
-
- try {
- String existingString = pSource.substring(pPosition + 1, pPosition + pSubstring.length() + 1);
-
- if (!existingString.equalsIgnoreCase(pSubstring)) {
- newString.delete(pPosition, pPosition + pSubstring.length());
- }
- }
- catch (Exception e) {
- // Do something!?
- }
- return newString.toString();
- }
-
- /**
- * Gets the first substring between the given string boundaries.
- *
- *
- * @param pSource The source string.
- * @param pBeginBoundaryString The string that marks the beginning.
- * @param pEndBoundaryString The string that marks the end.
- * @param pOffset The index to start searching in the source
- * string. If it is less than 0, the index will be set to 0.
- * @return the substring demarcated by the given string boundaries or null
- * if not both string boundaries are found.
- */
- public static String substring(final String pSource, final String pBeginBoundaryString, final String pEndBoundaryString,
- final int pOffset) {
- // Check offset
- int offset = (pOffset < 0)
- ? 0
- : pOffset;
-
- // Find the start index
- int startIndex = pSource.indexOf(pBeginBoundaryString, offset) + pBeginBoundaryString.length();
-
- if (startIndex < 0) {
- return null;
- }
-
- // Find the end index
- int endIndex = pSource.indexOf(pEndBoundaryString, startIndex);
-
- if (endIndex < 0) {
- return null;
- }
- return pSource.substring(startIndex, endIndex);
- }
-
- /**
- * Removes the first substring demarcated by the given string boundaries.
- *
- *
- * @param pSource The source string.
- * @param pBeginBoundaryChar The character that marks the beginning of the
- * unwanted substring.
- * @param pEndBoundaryChar The character that marks the end of the
- * unwanted substring.
- * @param pOffset The index to start searching in the source
- * string. If it is less than 0, the index will be set to 0.
- * @return the source string with all the demarcated substrings removed,
- * included the demarcation characters.
- * @deprecated this method actually removes all demarcated substring.. doesn't it?
- */
-
- /*public*/
- static String removeSubstring(final String pSource, final char pBeginBoundaryChar, final char pEndBoundaryChar, final int pOffset) {
- StringBuilder filteredString = new StringBuilder();
- boolean insideDemarcatedArea = false;
- char[] charArray = pSource.toCharArray();
-
- for (char c : charArray) {
- if (!insideDemarcatedArea) {
- if (c == pBeginBoundaryChar) {
- insideDemarcatedArea = true;
- }
- else {
- filteredString.append(c);
- }
- }
- else {
- if (c == pEndBoundaryChar) {
- insideDemarcatedArea = false;
- }
- }
- }
- return filteredString.toString();
- }
-
- /**
- * Removes all substrings demarcated by the given string boundaries.
- *
- *
- * @param pSource The source string.
- * @param pBeginBoundaryChar The character that marks the beginning of the unwanted substring.
- * @param pEndBoundaryChar The character that marks the end of the unwanted substring.
- * @return the source string with all the demarcated substrings removed, included the demarcation characters.
- */
- /*public*/
- static String removeSubstrings(final String pSource, final char pBeginBoundaryChar, final char pEndBoundaryChar) {
- StringBuilder filteredString = new StringBuilder();
- boolean insideDemarcatedArea = false;
- char[] charArray = pSource.toCharArray();
-
- for (char c : charArray) {
- if (!insideDemarcatedArea) {
- if (c == pBeginBoundaryChar) {
- insideDemarcatedArea = true;
- }
- else {
- filteredString.append(c);
- }
- }
- else {
- if (c == pEndBoundaryChar) {
- insideDemarcatedArea = false;
- }
- }
- }
- return filteredString.toString();
- }
-
- /**
- * Gets the first element of a {@code String} containing string elements delimited by a given delimiter.
- * NB - Straightforward implementation!
- *
- *
- * @param pSource The source string.
- * @param pDelimiter The delimiter used in the source string.
- * @return The last string element.
- * @todo This method should be re-implemented for more efficient execution.
- */
- public static String getFirstElement(final String pSource, final String pDelimiter) {
- if (pDelimiter == null) {
- throw new IllegalArgumentException("delimiter == null");
- }
-
- if (StringUtil.isEmpty(pSource)) {
- return pSource;
- }
-
- int idx = pSource.indexOf(pDelimiter);
- if (idx >= 0) {
- return pSource.substring(0, idx);
- }
- return pSource;
- }
-
- /**
- * Gets the last element of a {@code String} containing string elements
- * delimited by a given delimiter.
- * NB - Straightforward implementation!
- *
- *
- * @param pSource The source string.
- * @param pDelimiter The delimiter used in the source string.
- * @return The last string element.
- */
- public static String getLastElement(final String pSource, final String pDelimiter) {
- if (pDelimiter == null) {
- throw new IllegalArgumentException("delimiter == null");
- }
-
- if (StringUtil.isEmpty(pSource)) {
- return pSource;
- }
- int idx = pSource.lastIndexOf(pDelimiter);
- if (idx >= 0) {
- return pSource.substring(idx + 1);
- }
- return pSource;
- }
-
- /**
- * Converts a string array to a string of comma-separated values.
- *
- * @param pStringArray the string array
- * @return A string of comma-separated values
- */
- public static String toCSVString(Object[] pStringArray) {
- return toCSVString(pStringArray, ", ");
- }
-
- /**
- * Converts a string array to a string separated by the given delimiter.
- *
- * @param pStringArray the string array
- * @param pDelimiterString the delimiter string
- * @return string of delimiter separated values
- * @throws IllegalArgumentException if {@code pDelimiterString == null}
- */
- public static String toCSVString(Object[] pStringArray, String pDelimiterString) {
- if (pStringArray == null) {
- return "";
- }
- if (pDelimiterString == null) {
- throw new IllegalArgumentException("delimiter == null");
- }
-
- StringBuilder buffer = new StringBuilder();
- for (int i = 0; i < pStringArray.length; i++) {
- if (i > 0) {
- buffer.append(pDelimiterString);
- }
-
- buffer.append(pStringArray[i]);
- }
-
- return buffer.toString();
- }
-
- /**
- * @param pObject the object
- * @return a deep string representation of the given object
- */
- public static String deepToString(Object pObject) {
- return deepToString(pObject, false, 1);
- }
-
- /**
- * @param pObject the object
- * @param pDepth the maximum depth
- * @param pForceDeep {@code true} to force deep {@code toString}, even
- * if object overrides toString
- * @return a deep string representation of the given object
- * @todo Array handling (print full type and length)
- * @todo Register handlers for specific toDebugString handling? :-)
- */
- public static String deepToString(Object pObject, boolean pForceDeep, int pDepth) {
- // Null is null
- if (pObject == null) {
- return null;
- }
-
- // Implements toString, use it as-is unless pForceDeep
- if (!pForceDeep && !isIdentityToString(pObject)) {
- return pObject.toString();
- }
-
- StringBuilder buffer = new StringBuilder();
-
- if (pObject.getClass().isArray()) {
- // Special array handling
- Class componentClass = pObject.getClass();
- while (componentClass.isArray()) {
- buffer.append('[');
- buffer.append(Array.getLength(pObject));
- buffer.append(']');
- componentClass = componentClass.getComponentType();
- }
- buffer.insert(0, componentClass);
- buffer.append(" {hashCode=");
- buffer.append(Integer.toHexString(pObject.hashCode()));
- buffer.append("}");
- }
- else {
- // Append toString value only if overridden
- if (isIdentityToString(pObject)) {
- buffer.append(" {");
- }
- else {
- buffer.append(" {toString=");
- buffer.append(pObject.toString());
- buffer.append(", ");
- }
- buffer.append("hashCode=");
- buffer.append(Integer.toHexString(pObject.hashCode()));
- // Loop through, and filter out any getters
- Method[] methods = pObject.getClass().getMethods();
- for (Method method : methods) {
- // Filter only public methods
- if (Modifier.isPublic(method.getModifiers())) {
- String methodName = method.getName();
-
- // Find name of property
- String name = null;
- if (!methodName.equals("getClass")
- && methodName.length() > 3 && methodName.startsWith("get")
- && Character.isUpperCase(methodName.charAt(3))) {
- name = methodName.substring(3);
- }
- else if (methodName.length() > 2 && methodName.startsWith("is")
- && Character.isUpperCase(methodName.charAt(2))) {
- name = methodName.substring(2);
- }
-
- if (name != null) {
- // If lowercase name, convert, else keep case
- if (name.length() > 1 && Character.isLowerCase(name.charAt(1))) {
- name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
- }
-
- Class[] paramTypes = method.getParameterTypes();// involves array copying...
- boolean hasParams = (paramTypes != null && paramTypes.length > 0);
- boolean isVoid = Void.TYPE.equals(method.getReturnType());
-
- // Filter return type & parameters
- if (!isVoid && !hasParams) {
- try {
- Object value = method.invoke(pObject);
- buffer.append(", ");
- buffer.append(name);
- buffer.append('=');
- if (pDepth != 0 && value != null && isIdentityToString(value)) {
- buffer.append(deepToString(value, pForceDeep, pDepth > 0 ? pDepth - 1 : -1));
- }
- else {
- buffer.append(value);
- }
- }
- catch (Exception e) {
- // Next..!
- }
- }
- }
- }
- }
- buffer.append('}');
-
- // Get toString from original object
- buffer.insert(0, pObject.getClass().getName());
- }
-
- return buffer.toString();
- }
-
- /**
- * Tests if the {@code toString} method of the given object is inherited
- * from {@code Object}.
- *
- * @param pObject the object
- * @return {@code true} if toString of class Object
- */
- private static boolean isIdentityToString(Object pObject) {
- try {
- Method toString = pObject.getClass().getMethod("toString");
- if (toString.getDeclaringClass() == Object.class) {
- return true;
- }
- }
- catch (Exception ignore) {
- // Ignore
- }
-
- return false;
- }
-
- /**
- * Returns a string on the same format as {@code Object.toString()}.
- *
- * @param pObject the object
- * @return the object as a {@code String} on the format of
- * {@code Object.toString()}
- */
- public static String identityToString(Object pObject) {
- if (pObject == null) {
- return null;
- }
- else {
- return pObject.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(pObject));
- }
- }
-
- /**
- * Tells whether or not the given string string matches the given regular
- * expression.
- *
- * An invocation of this method of the form
- * matches(str, regex) yields exactly the
- * same result as the expression
- *
- * {@link Pattern}.
- * {@link Pattern#matches(String, CharSequence) matches}
- * (regex, str)
- *
- * @param pString the string
- * @param pRegex the regular expression to which this string is to be matched
- * @return {@code true} if, and only if, this string matches the
- * given regular expression
- * @throws PatternSyntaxException if the regular expression's syntax is invalid
- * @see Pattern
- * @see String#matches(String)
- */
- public boolean matches(String pString, String pRegex) throws PatternSyntaxException {
- return Pattern.matches(pRegex, pString);
- }
-
- /**
- * Replaces the first substring of the given string that matches the given
- * regular expression with the given pReplacement.
- *
- * An invocation of this method of the form
- * replaceFirst(str, regex, repl)
- * yields exactly the same result as the expression
- *
- *
- * {@link Pattern}.{@link Pattern#compile compile}(regex).
- * {@link Pattern#matcher matcher}(str).
- * {@link java.util.regex.Matcher#replaceFirst replaceFirst}(repl)
- *
- * @param pString the string
- * @param pRegex the regular expression to which this string is to be matched
- * @param pReplacement the replacement text
- * @return The resulting {@code String}
- * @throws PatternSyntaxException if the regular expression's syntax is invalid
- * @see Pattern
- * @see java.util.regex.Matcher#replaceFirst(String)
- */
- public String replaceFirst(String pString, String pRegex, String pReplacement) {
- return Pattern.compile(pRegex).matcher(pString).replaceFirst(pReplacement);
- }
-
- /**
- * Replaces each substring of this string that matches the given
- * regular expression with the given pReplacement.
- *
- * An invocation of this method of the form
- * replaceAll(str, pRegex, repl<)
- * yields exactly the same result as the expression
- *
- *
- * {@link Pattern}.{@link Pattern#compile compile}(pRegex).
- * {@link Pattern#matcher matcher}(str{@code ).
- * {@link java.util.regex.Matcher#replaceAll replaceAll}(}repl{@code )}
- *
- * @param pString the string
- * @param pRegex the regular expression to which this string is to be matched
- * @param pReplacement the replacement string
- * @return The resulting {@code String}
- * @throws PatternSyntaxException if the regular expression's syntax is invalid
- * @see Pattern
- * @see String#replaceAll(String,String)
- */
- public String replaceAll(String pString, String pRegex, String pReplacement) {
- return Pattern.compile(pRegex).matcher(pString).replaceAll(pReplacement);
- }
-
- /**
- * Splits this string around matches of the given regular expression.
- *
- * The array returned by this method contains each substring of this
- * string that is terminated by another substring that matches the given
- * expression or is terminated by the end of the string. The substrings in
- * the array are in the order in which they occur in this string. If the
- * expression does not match any part of the input then the resulting array
- * has just one element, namely this string.
- *
- * The {@code pLimit} parameter controls the number of times the
- * pattern is applied and therefore affects the length of the resulting
- * array. If the pLimit n is greater than zero then the pattern
- * will be applied at most n - 1 times, the array's
- * length will be no greater than n, and the array's last entry
- * will contain all input beyond the last matched delimiter. If n
- * is non-positive then the pattern will be applied as many times as
- * possible and the array can have any length. If n is zero then
- * the pattern will be applied as many times as possible, the array can
- * have any length, and trailing empty strings will be discarded.
- *
- * An invocation of this method of the form
- * split(str, regex, n)
- * yields the same result as the expression
- *
- * {@link Pattern}.
- * {@link Pattern#compile compile}(regex).
- * {@link Pattern#split(CharSequence,int) split}(str, n)
- *
- *
- * @param pString the string
- * @param pRegex the delimiting regular expression
- * @param pLimit the result threshold, as described above
- * @return the array of strings computed by splitting this string
- * around matches of the given regular expression
- * @throws PatternSyntaxException
- * if the regular expression's syntax is invalid
- * @see Pattern
- * @see String#split(String,int)
- */
- public String[] split(String pString, String pRegex, int pLimit) {
- return Pattern.compile(pRegex).split(pString, pLimit);
- }
-
- /**
- * Splits this string around matches of the given regular expression.
- *
- * This method works as if by invoking the two-argument
- * {@link #split(String,String,int) split} method with the given
- * expression and a limit argument of zero.
- * Trailing empty strings are therefore not included in the resulting array.
- *
- * @param pString the string
- * @param pRegex the delimiting regular expression
- * @return the array of strings computed by splitting this string
- * around matches of the given regular expression
- * @throws PatternSyntaxException if the regular expression's syntax is invalid
- * @see Pattern
- * @see String#split(String)
- */
- public String[] split(String pString, String pRegex) {
- return split(pString, pRegex, 0);
- }
-
- /**
- * Converts the input string
- * from camel-style (Java in-fix) naming convention
- * to Lisp-style naming convention (hyphen delimitted, all lower case).
- * Other characters in the string are left untouched.
- *
- * Eg.
- * {@code "foo" => "foo"},
- * {@code "fooBar" => "foo-bar"},
- * {@code "myURL" => "my-url"},
- * {@code "HttpRequestWrapper" => "http-request-wrapper"}
- * {@code "HttpURLConnection" => "http-url-connection"}
- * {@code "my45Caliber" => "my-45-caliber"}
- * {@code "allready-lisp" => "allready-lisp"}
- *
- * @param pString the camel-style input string
- * @return the string converted to lisp-style naming convention
- * @throws IllegalArgumentException if {@code pString == null}
- * @see #lispToCamel(String)
- */
- // TODO: RefactorMe!
- public static String camelToLisp(final String pString) {
- if (pString == null) {
- throw new IllegalArgumentException("string == null");
- }
- if (pString.length() == 0) {
- return pString;
- }
-
- StringBuilder buf = null;
- int lastPos = 0;
- boolean inCharSequence = false;
- boolean inNumberSequence = false;
-
- // NOTE: Start at index 1, as first letter should never be hyphen
- for (int i = 1; i < pString.length(); i++) {
- char current = pString.charAt(i);
- if (Character.isUpperCase(current)) {
- // Init buffer if necessary
- if (buf == null) {
- buf = new StringBuilder(pString.length() + 3);// Allow for some growth
- }
-
- if (inNumberSequence) {
- // Sequence end
- inNumberSequence = false;
-
- buf.append(pString.substring(lastPos, i));
- if (current != '-') {
- buf.append('-');
- }
- lastPos = i;
- continue;
- }
-
- // Treat multiple uppercase chars as single word
- char previous = pString.charAt(i - 1);
- if (i == lastPos || Character.isUpperCase(previous)) {
- inCharSequence = true;
- continue;
- }
-
- // Append word
- buf.append(pString.substring(lastPos, i).toLowerCase());
- if (previous != '-') {
- buf.append('-');
- }
- buf.append(Character.toLowerCase(current));
-
- lastPos = i + 1;
- }
- else if (Character.isDigit(current)) {
- // Init buffer if necessary
- if (buf == null) {
- buf = new StringBuilder(pString.length() + 3);// Allow for some growth
- }
-
- if (inCharSequence) {
- // Sequence end
- inCharSequence = false;
-
- buf.append(pString.substring(lastPos, i).toLowerCase());
- if (current != '-') {
- buf.append('-');
- }
- lastPos = i;
- continue;
- }
-
- // Treat multiple digits as single word
- char previous = pString.charAt(i - 1);
- if (i == lastPos || Character.isDigit(previous)) {
- inNumberSequence = true;
- continue;
- }
-
- // Append word
- buf.append(pString.substring(lastPos, i).toLowerCase());
- if (previous != '-') {
- buf.append('-');
- }
- buf.append(Character.toLowerCase(current));
-
- lastPos = i + 1;
- }
- else if (inNumberSequence) {
- // Sequence end
- inNumberSequence = false;
-
- buf.append(pString.substring(lastPos, i));
- if (current != '-') {
- buf.append('-');
- }
- lastPos = i;
- }
- else if (inCharSequence) {
- // Sequence end
- inCharSequence = false;
-
- // NOTE: Special treatment! Last upper case, is first char in
- // next word, not last char in this word
- buf.append(pString.substring(lastPos, i - 1).toLowerCase());
- if (current != '-') {
- buf.append('-');
- }
- lastPos = i - 1;
- }
- }
-
- if (buf != null) {
- // Append the rest
- buf.append(pString.substring(lastPos).toLowerCase());
- return buf.toString();
- }
- else {
- return Character.isUpperCase(pString.charAt(0)) ? pString.toLowerCase() : pString;
- }
- }
-
- /**
- * Converts the input string
- * from Lisp-style naming convention (hyphen delimitted, all lower case)
- * to camel-style (Java in-fix) naming convention.
- * Other characters in the string are left untouched.
- *
- * Eg.
- * {@code "foo" => "foo"},
- * {@code "foo-bar" => "fooBar"},
- * {@code "http-request-wrapper" => "httpRequestWrapper"}
- * {@code "my-45-caliber" => "my45Caliber"}
- * {@code "allreadyCamel" => "allreadyCamel"}
- *
- *
- * @param pString the lisp-style input string
- * @return the string converted to camel-style
- * @throws IllegalArgumentException if {@code pString == null}
- * @see #lispToCamel(String,boolean)
- * @see #camelToLisp(String)
- */
- public static String lispToCamel(final String pString) {
- return lispToCamel(pString, false);
- }
-
- /**
- * Converts the input string
- * from Lisp-style naming convention (hyphen delimitted, all lower case)
- * to camel-style (Java in-fix) naming convention.
- * Other characters in the string are left untouched.
- *
- * To create a string starting with a lower case letter
- * (like Java variable names, etc),
- * specify the {@code pFirstUpperCase} paramter to be {@code false}.
- * Eg.
- * {@code "foo" => "foo"},
- * {@code "foo-bar" => "fooBar"},
- * {@code "allreadyCamel" => "allreadyCamel"}
- *
- * To create a string starting with an upper case letter
- * (like Java class name, etc),
- * specify the {@code pFirstUpperCase} paramter to be {@code true}.
- * Eg.
- * {@code "http-request-wrapper" => "HttpRequestWrapper"}
- * {@code "my-45-caliber" => "My45Caliber"}
- *
- *
- * @param pString the lisp-style input string
- * @param pFirstUpperCase {@code true} if the first char should be
- * upper case
- * @return the string converted to camel-style
- * @throws IllegalArgumentException if {@code pString == null}
- * @see #camelToLisp(String)
- */
- public static String lispToCamel(final String pString, final boolean pFirstUpperCase) {
- if (pString == null) {
- throw new IllegalArgumentException("string == null");
- }
- if (pString.length() == 0) {
- return pString;
- }
-
- StringBuilder buf = null;
- int lastPos = 0;
-
- for (int i = 0; i < pString.length(); i++) {
- char current = pString.charAt(i);
- if (current == '-') {
-
- // Init buffer if necessary
- if (buf == null) {
- buf = new StringBuilder(pString.length() - 1);// Can't be larger
- }
-
- // Append with upper case
- if (lastPos != 0 || pFirstUpperCase) {
- buf.append(Character.toUpperCase(pString.charAt(lastPos)));
- lastPos++;
- }
-
- buf.append(pString.substring(lastPos, i).toLowerCase());
- lastPos = i + 1;
- }
- }
-
- if (buf != null) {
- buf.append(Character.toUpperCase(pString.charAt(lastPos)));
- buf.append(pString.substring(lastPos + 1).toLowerCase());
- return buf.toString();
- }
- else {
- if (pFirstUpperCase && !Character.isUpperCase(pString.charAt(0))) {
- return capitalize(pString, 0);
- }
- else
- if (!pFirstUpperCase && Character.isUpperCase(pString.charAt(0))) {
- return Character.toLowerCase(pString.charAt(0)) + pString.substring(1);
- }
-
- return pString;
- }
- }
-
- public static String reverse(final String pString) {
- final char[] chars = new char[pString.length()];
- pString.getChars(0, chars.length, chars, 0);
-
- for (int i = 0; i < chars.length / 2; i++) {
- char temp = chars[i];
- chars[i] = chars[chars.length - 1 - i];
- chars[chars.length - 1 - i] = temp;
- }
-
- return new String(chars);
- }
+/*
+ * 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.lang;
+
+import com.twelvemonkeys.util.StringTokenIterator;
+
+import java.awt.*;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.charset.UnsupportedCharsetException;
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * A utility class with some useful string manipulation methods.
+ *
+ * @author Harald Kuhr
+ * @author Eirik Torske
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/StringUtil.java#2 $
+ * return values, null-value handling and parameter names (cosmetics).
+ */
+// TODO: Consistency check: Method names, parameter sequence, Exceptions,
+public final class StringUtil {
+
+ /**
+ * The default delimiter string, used by the {@code toXXXArray()}
+ * methods.
+ * Its value is {@code ", \t\n\r\f"}.
+ *
+ *
+ * @see #toStringArray(String)
+ * @see #toIntArray(String)
+ * @see #toLongArray(String)
+ * @see #toDoubleArray(String)
+ */
+ public final static String DELIMITER_STRING = ", \t\n\r\f";
+
+ // Avoid constructor showing up in API doc
+ private StringUtil() {
+ }
+
+ /**
+ * Constructs a new {@link String} by decoding the specified sub array of bytes using the specified charset.
+ * Replacement for {@link String#String(byte[], int, int, String) new String(byte[], int, int, String)}, that does
+ * not throw the checked {@link UnsupportedEncodingException},
+ * but instead the unchecked {@link UnsupportedCharsetException} if the character set is not supported.
+ *
+ * @param pData the bytes to be decoded to characters
+ * @param pOffset the index of the first byte to decode
+ * @param pLength the number of bytes to decode
+ * @param pCharset the name of a supported character set
+ * @return a newly created string.
+ * @throws UnsupportedCharsetException
+ *
+ * @see String#String(byte[], int, int, String)
+ */
+ public static String decode(final byte[] pData, final int pOffset, final int pLength, final String pCharset) {
+ try {
+ return new String(pData, pOffset, pLength, pCharset);
+ }
+ catch (UnsupportedEncodingException e) {
+ throw new UnsupportedCharsetException(pCharset);
+ }
+ }
+
+ /**
+ * Returns the value of the given {@code Object}, as a {@code String}.
+ * Unlike String.valueOf, this method returns {@code null}
+ * instead of the {@code String} "null", if {@code null} is given as
+ * the argument.
+ *
+ * @param pObj the Object to find the {@code String} value of.
+ * @return the String value of the given object, or {@code null} if the
+ * {@code pObj} == {@code null}.
+ * @see String#valueOf(Object)
+ * @see String#toString()
+ */
+ public static String valueOf(Object pObj) {
+ return ((pObj != null) ? pObj.toString() : null);
+ }
+
+ /**
+ * Converts a string to uppercase.
+ *
+ * @param pString the string to convert
+ * @return the string converted to uppercase, or null if the argument was
+ * null.
+ */
+ public static String toUpperCase(String pString) {
+ if (pString != null) {
+ return pString.toUpperCase();
+ }
+ return null;
+ }
+
+ /**
+ * Converts a string to lowercase.
+ *
+ * @param pString the string to convert
+ * @return the string converted to lowercase, or null if the argument was
+ * null.
+ */
+ public static String toLowerCase(String pString) {
+ if (pString != null) {
+ return pString.toLowerCase();
+ }
+ return null;
+ }
+
+ /**
+ * Tests if a String is null, or contains nothing but white-space.
+ *
+ * @param pString The string to test
+ * @return true if the string is null or contains only whitespace,
+ * otherwise false.
+ */
+ public static boolean isEmpty(String pString) {
+ return ((pString == null) || (pString.trim().length() == 0));
+ }
+
+ /**
+ * Tests a string array, to see if all items are null or an empty string.
+ *
+ * @param pStringArray The string array to check.
+ * @return true if the string array is null or only contains string items
+ * that are null or contain only whitespace, otherwise false.
+ */
+ public static boolean isEmpty(String[] pStringArray) {
+ // No elements to test
+ if (pStringArray == null) {
+ return true;
+ }
+
+ // Test all the elements
+ for (String string : pStringArray) {
+ if (!isEmpty(string)) {
+ return false;
+ }
+ }
+
+ // All elements are empty
+ return true;
+ }
+
+ /**
+ * Tests if a string contains another string.
+ *
+ * @param pContainer The string to test
+ * @param pLookFor The string to look for
+ * @return {@code true} if the container string is contains the string, and
+ * both parameters are non-{@code null}, otherwise {@code false}.
+ */
+ public static boolean contains(String pContainer, String pLookFor) {
+ return ((pContainer != null) && (pLookFor != null) && (pContainer.indexOf(pLookFor) >= 0));
+ }
+
+ /**
+ * Tests if a string contains another string, ignoring case.
+ *
+ * @param pContainer The string to test
+ * @param pLookFor The string to look for
+ * @return {@code true} if the container string is contains the string, and
+ * both parameters are non-{@code null}, otherwise {@code false}.
+ * @see #contains(String,String)
+ */
+ public static boolean containsIgnoreCase(String pContainer, String pLookFor) {
+ return indexOfIgnoreCase(pContainer, pLookFor, 0) >= 0;
+ }
+
+ /**
+ * Tests if a string contains a specific character.
+ *
+ * @param pString The string to check.
+ * @param pChar The character to search for.
+ * @return true if the string contains the specific character.
+ */
+ public static boolean contains(final String pString, final int pChar) {
+ return ((pString != null) && (pString.indexOf(pChar) >= 0));
+ }
+
+ /**
+ * Tests if a string contains a specific character, ignoring case.
+ *
+ * @param pString The string to check.
+ * @param pChar The character to search for.
+ * @return true if the string contains the specific character.
+ */
+ public static boolean containsIgnoreCase(String pString, int pChar) {
+ return ((pString != null)
+ && ((pString.indexOf(Character.toLowerCase((char) pChar)) >= 0)
+ || (pString.indexOf(Character.toUpperCase((char) pChar)) >= 0)));
+
+ // NOTE: I don't convert the string to uppercase, but instead test
+ // the string (potentially) two times, as this is more efficient for
+ // long strings (in most cases).
+ }
+
+ /**
+ * Returns the index within this string of the first occurrence of the
+ * specified substring.
+ *
+ * @param pString The string to test
+ * @param pLookFor The string to look for
+ * @return if the string argument occurs as a substring within this object,
+ * then the index of the first character of the first such substring is
+ * returned; if it does not occur as a substring, -1 is returned.
+ * @see String#indexOf(String)
+ */
+ public static int indexOfIgnoreCase(String pString, String pLookFor) {
+ return indexOfIgnoreCase(pString, pLookFor, 0);
+ }
+
+ /**
+ * Returns the index within this string of the first occurrence of the
+ * specified substring, starting at the specified index.
+ *
+ * @param pString The string to test
+ * @param pLookFor The string to look for
+ * @param pPos The first index to test
+ * @return if the string argument occurs as a substring within this object,
+ * then the index of the first character of the first such substring is
+ * returned; if it does not occur as a substring, -1 is returned.
+ * @see String#indexOf(String,int)
+ */
+ public static int indexOfIgnoreCase(String pString, String pLookFor, int pPos) {
+ if ((pString == null) || (pLookFor == null)) {
+ return -1;
+ }
+ if (pLookFor.length() == 0) {
+ return pPos;// All strings "contains" the empty string
+ }
+ if (pLookFor.length() > pString.length()) {
+ return -1;// Cannot contain string longer than itself
+ }
+
+ // Get first char
+ char firstL = Character.toLowerCase(pLookFor.charAt(0));
+ char firstU = Character.toUpperCase(pLookFor.charAt(0));
+ int indexLower = 0;
+ int indexUpper = 0;
+
+ for (int i = pPos; i <= (pString.length() - pLookFor.length()); i++) {
+
+ // Peek for first char
+ indexLower = ((indexLower >= 0) && (indexLower <= i))
+ ? pString.indexOf(firstL, i)
+ : indexLower;
+ indexUpper = ((indexUpper >= 0) && (indexUpper <= i))
+ ? pString.indexOf(firstU, i)
+ : indexUpper;
+ if (indexLower < 0) {
+ if (indexUpper < 0) {
+ return -1;// First char not found
+ }
+ else {
+ i = indexUpper;// Only upper
+ }
+ }
+ else if (indexUpper < 0) {
+ i = indexLower;// Only lower
+ }
+ else {
+
+ // Both found, select first occurence
+ i = (indexLower < indexUpper)
+ ? indexLower
+ : indexUpper;
+ }
+
+ // Only one?
+ if (pLookFor.length() == 1) {
+ return i;// The only char found!
+ }
+
+ // Test if we still have enough chars
+ else if (i > (pString.length() - pLookFor.length())) {
+ return -1;
+ }
+
+ // Test if last char equals! (regionMatches is expensive)
+ else if ((pString.charAt(i + pLookFor.length() - 1) != Character.toLowerCase(pLookFor.charAt(pLookFor.length() - 1)))
+ && (pString.charAt(i + pLookFor.length() - 1) != Character.toUpperCase(pLookFor.charAt(pLookFor.length() - 1)))) {
+ continue;// Nope, try next
+ }
+
+ // Test from second char, until second-last char
+ else if ((pLookFor.length() <= 2) || pString.regionMatches(true, i + 1, pLookFor, 1, pLookFor.length() - 2)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the index within this string of the rightmost occurrence of the
+ * specified substring. The rightmost empty string "" is considered to
+ * occur at the index value {@code pString.length() - 1}.
+ *
+ * @param pString The string to test
+ * @param pLookFor The string to look for
+ * @return If the string argument occurs one or more times as a substring
+ * within this object at a starting index no greater than fromIndex, then
+ * the index of the first character of the last such substring is returned.
+ * If it does not occur as a substring starting at fromIndex or earlier, -1
+ * is returned.
+ * @see String#lastIndexOf(String)
+ */
+ public static int lastIndexOfIgnoreCase(String pString, String pLookFor) {
+ return lastIndexOfIgnoreCase(pString, pLookFor, pString != null ? pString.length() - 1 : -1);
+ }
+
+ /**
+ * Returns the index within this string of the rightmost occurrence of the
+ * specified substring. The rightmost empty string "" is considered to
+ * occur at the index value {@code pPos}
+ *
+ * @param pString The string to test
+ * @param pLookFor The string to look for
+ * @param pPos The last index to test
+ * @return If the string argument occurs one or more times as a substring
+ * within this object at a starting index no greater than fromIndex, then
+ * the index of the first character of the last such substring is returned.
+ * If it does not occur as a substring starting at fromIndex or earlier, -1
+ * is returned.
+ * @see String#lastIndexOf(String,int)
+ */
+ public static int lastIndexOfIgnoreCase(String pString, String pLookFor, int pPos) {
+ if ((pString == null) || (pLookFor == null)) {
+ return -1;
+ }
+ if (pLookFor.length() == 0) {
+ return pPos;// All strings "contains" the empty string
+ }
+ if (pLookFor.length() > pString.length()) {
+ return -1;// Cannot contain string longer than itself
+ }
+
+ // Get first char
+ char firstL = Character.toLowerCase(pLookFor.charAt(0));
+ char firstU = Character.toUpperCase(pLookFor.charAt(0));
+ int indexLower = pPos;
+ int indexUpper = pPos;
+
+ for (int i = pPos; i >= 0; i--) {
+
+ // Peek for first char
+ indexLower = ((indexLower >= 0) && (indexLower >= i))
+ ? pString.lastIndexOf(firstL, i)
+ : indexLower;
+ indexUpper = ((indexUpper >= 0) && (indexUpper >= i))
+ ? pString.lastIndexOf(firstU, i)
+ : indexUpper;
+ if (indexLower < 0) {
+ if (indexUpper < 0) {
+ return -1;// First char not found
+ }
+ else {
+ i = indexUpper;// Only upper
+ }
+ }
+ else if (indexUpper < 0) {
+ i = indexLower;// Only lower
+ }
+ else {
+
+ // Both found, select last occurence
+ i = (indexLower > indexUpper)
+ ? indexLower
+ : indexUpper;
+ }
+
+ // Only one?
+ if (pLookFor.length() == 1) {
+ return i;// The only char found!
+ }
+
+ // Test if we still have enough chars
+ else if (i > (pString.length() - pLookFor.length())) {
+ //return -1;
+ continue;
+ }
+
+ // Test if last char equals! (regionMatches is expensive)
+ else
+ if ((pString.charAt(i + pLookFor.length() - 1) != Character.toLowerCase(pLookFor.charAt(pLookFor.length() - 1)))
+ && (pString.charAt(i + pLookFor.length() - 1) != Character.toUpperCase(pLookFor.charAt(pLookFor.length() - 1)))) {
+ continue;// Nope, try next
+ }
+
+ // Test from second char, until second-last char
+ else
+ if ((pLookFor.length() <= 2) || pString.regionMatches(true, i + 1, pLookFor, 1, pLookFor.length() - 2)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the index within this string of the first occurrence of the
+ * specified character.
+ *
+ * @param pString The string to test
+ * @param pChar The character to look for
+ * @return if the string argument occurs as a substring within this object,
+ * then the index of the first character of the first such substring is
+ * returned; if it does not occur as a substring, -1 is returned.
+ * @see String#indexOf(int)
+ */
+ public static int indexOfIgnoreCase(String pString, int pChar) {
+ return indexOfIgnoreCase(pString, pChar, 0);
+ }
+
+ /**
+ * Returns the index within this string of the first occurrence of the
+ * specified character, starting at the specified index.
+ *
+ * @param pString The string to test
+ * @param pChar The character to look for
+ * @param pPos The first index to test
+ * @return if the string argument occurs as a substring within this object,
+ * then the index of the first character of the first such substring is
+ * returned; if it does not occur as a substring, -1 is returned.
+ * @see String#indexOf(int,int)
+ */
+ public static int indexOfIgnoreCase(String pString, int pChar, int pPos) {
+ if ((pString == null)) {
+ return -1;
+ }
+
+ // Get first char
+ char lower = Character.toLowerCase((char) pChar);
+ char upper = Character.toUpperCase((char) pChar);
+ int indexLower;
+ int indexUpper;
+
+ // Test for char
+ indexLower = pString.indexOf(lower, pPos);
+ indexUpper = pString.indexOf(upper, pPos);
+ if (indexLower < 0) {
+
+ /* if (indexUpper < 0)
+ return -1; // First char not found
+ else */
+ return indexUpper;// Only upper
+ }
+ else if (indexUpper < 0) {
+ return indexLower;// Only lower
+ }
+ else {
+
+ // Both found, select first occurence
+ return (indexLower < indexUpper)
+ ? indexLower
+ : indexUpper;
+ }
+ }
+
+ /**
+ * Returns the index within this string of the last occurrence of the
+ * specified character.
+ *
+ * @param pString The string to test
+ * @param pChar The character to look for
+ * @return if the string argument occurs as a substring within this object,
+ * then the index of the first character of the first such substring is
+ * returned; if it does not occur as a substring, -1 is returned.
+ * @see String#lastIndexOf(int)
+ */
+ public static int lastIndexOfIgnoreCase(String pString, int pChar) {
+ return lastIndexOfIgnoreCase(pString, pChar, pString != null ? pString.length() : -1);
+ }
+
+ /**
+ * Returns the index within this string of the last occurrence of the
+ * specified character, searching backward starting at the specified index.
+ *
+ * @param pString The string to test
+ * @param pChar The character to look for
+ * @param pPos The last index to test
+ * @return if the string argument occurs as a substring within this object,
+ * then the index of the first character of the first such substring is
+ * returned; if it does not occur as a substring, -1 is returned.
+ * @see String#lastIndexOf(int,int)
+ */
+ public static int lastIndexOfIgnoreCase(String pString, int pChar, int pPos) {
+ if ((pString == null)) {
+ return -1;
+ }
+
+ // Get first char
+ char lower = Character.toLowerCase((char) pChar);
+ char upper = Character.toUpperCase((char) pChar);
+ int indexLower;
+ int indexUpper;
+
+ // Test for char
+ indexLower = pString.lastIndexOf(lower, pPos);
+ indexUpper = pString.lastIndexOf(upper, pPos);
+ if (indexLower < 0) {
+
+ /* if (indexUpper < 0)
+ return -1; // First char not found
+ else */
+ return indexUpper;// Only upper
+ }
+ else if (indexUpper < 0) {
+ return indexLower;// Only lower
+ }
+ else {
+
+ // Both found, select last occurence
+ return (indexLower > indexUpper)
+ ? indexLower
+ : indexUpper;
+ }
+ }
+
+ /**
+ * Trims the argument string for whitespace on the left side only.
+ *
+ * @param pString the string to trim
+ * @return the string with no whitespace on the left, or {@code null} if
+ * the string argument is {@code null}.
+ * @see #rtrim
+ * @see String#trim()
+ */
+ public static String ltrim(String pString) {
+ if ((pString == null) || (pString.length() == 0)) {
+ return pString;// Null or empty string
+ }
+ for (int i = 0; i < pString.length(); i++) {
+ if (!Character.isWhitespace(pString.charAt(i))) {
+ if (i == 0) {
+ return pString;// First char is not whitespace
+ }
+ else {
+ return pString.substring(i);// Return rest after whitespace
+ }
+ }
+ }
+
+ // If all whitespace, return empty string
+ return "";
+ }
+
+ /**
+ * Trims the argument string for whitespace on the right side only.
+ *
+ * @param pString the string to trim
+ * @return the string with no whitespace on the right, or {@code null} if
+ * the string argument is {@code null}.
+ * @see #ltrim
+ * @see String#trim()
+ */
+ public static String rtrim(String pString) {
+ if ((pString == null) || (pString.length() == 0)) {
+ return pString;// Null or empty string
+ }
+ for (int i = pString.length(); i > 0; i--) {
+ if (!Character.isWhitespace(pString.charAt(i - 1))) {
+ if (i == pString.length()) {
+ return pString;// First char is not whitespace
+ }
+ else {
+ return pString.substring(0, i);// Return before whitespace
+ }
+ }
+ }
+
+ // If all whitespace, return empty string
+ return "";
+ }
+
+ /**
+ * Replaces a substring of a string with another string. All matches are
+ * replaced.
+ *
+ * @param pSource The source String
+ * @param pPattern The pattern to replace
+ * @param pReplace The new String to be inserted instead of the
+ * replace String
+ * @return The new String with the pattern replaced
+ */
+ public static String replace(String pSource, String pPattern, String pReplace) {
+ if (pPattern.length() == 0) {
+ return pSource;// Special case: No pattern to replace
+ }
+
+ int match;
+ int offset = 0;
+ StringBuilder result = new StringBuilder();
+
+ // Loop string, until last occurence of pattern, and replace
+ while ((match = pSource.indexOf(pPattern, offset)) != -1) {
+ // Append everything until pattern
+ result.append(pSource.substring(offset, match));
+ // Append the replace string
+ result.append(pReplace);
+ offset = match + pPattern.length();
+ }
+
+ // Append rest of string and return
+ result.append(pSource.substring(offset));
+
+ return result.toString();
+ }
+
+ /**
+ * Replaces a substring of a string with another string, ignoring case.
+ * All matches are replaced.
+ *
+ * @param pSource The source String
+ * @param pPattern The pattern to replace
+ * @param pReplace The new String to be inserted instead of the
+ * replace String
+ * @return The new String with the pattern replaced
+ * @see #replace(String,String,String)
+ */
+ public static String replaceIgnoreCase(String pSource, String pPattern, String pReplace) {
+ if (pPattern.length() == 0) {
+ return pSource;// Special case: No pattern to replace
+ }
+ int match;
+ int offset = 0;
+ StringBuilder result = new StringBuilder();
+
+ while ((match = indexOfIgnoreCase(pSource, pPattern, offset)) != -1) {
+ result.append(pSource.substring(offset, match));
+ result.append(pReplace);
+ offset = match + pPattern.length();
+ }
+ result.append(pSource.substring(offset));
+ return result.toString();
+ }
+
+ /**
+ * Cuts a string between two words, before a sepcified length, if the
+ * string is longer than the maxium lenght. The string is optionally padded
+ * with the pad argument. The method assumes words to be separated by the
+ * space character (" ").
+ * Note that the maximum length argument is absolute, and will also include
+ * the length of the padding.
+ *
+ * @param pString The string to cut
+ * @param pMaxLen The maximum length before cutting
+ * @param pPad The string to append at the end, aftrer cutting
+ * @return The cutted string with padding, or the original string, if it
+ * was shorter than the max length.
+ * @see #pad(String,int,String,boolean)
+ */
+ public static String cut(String pString, int pMaxLen, String pPad) {
+ if (pString == null) {
+ return null;
+ }
+ if (pPad == null) {
+ pPad = "";
+ }
+ int len = pString.length();
+
+ if (len > pMaxLen) {
+ len = pString.lastIndexOf(' ', pMaxLen - pPad.length());
+ }
+ else {
+ return pString;
+ }
+ return pString.substring(0, len) + pPad;
+ }
+
+ /**
+ * Makes the Nth letter of a String uppercase. If the index is outside the
+ * the length of the argument string, the argument is simply returned.
+ *
+ * @param pString The string to capitalize
+ * @param pIndex The base-0 index of the char to capitalize.
+ * @return The capitalized string, or null, if a null argument was given.
+ */
+ public static String capitalize(String pString, int pIndex) {
+ if (pIndex < 0) {
+ throw new IndexOutOfBoundsException("Negative index not allowed: " + pIndex);
+ }
+ if (pString == null || pString.length() <= pIndex) {
+ return pString;
+ }
+
+ // This is the fastest method, according to my tests
+
+ // Skip array duplication if allready capitalized
+ if (Character.isUpperCase(pString.charAt(pIndex))) {
+ return pString;
+ }
+
+ // Convert to char array, capitalize and create new String
+ char[] charArray = pString.toCharArray();
+ charArray[pIndex] = Character.toUpperCase(charArray[pIndex]);
+ return new String(charArray);
+
+ /**
+ StringBuilder buf = new StringBuilder(pString);
+ buf.setCharAt(pIndex, Character.toUpperCase(buf.charAt(pIndex)));
+ return buf.toString();
+ //*/
+
+ /**
+ return pString.substring(0, pIndex)
+ + Character.toUpperCase(pString.charAt(pIndex))
+ + pString.substring(pIndex + 1);
+ //*/
+ }
+
+ /**
+ * Makes the first letter of a String uppercase.
+ *
+ * @param pString The string to capitalize
+ * @return The capitalized string, or null, if a null argument was given.
+ */
+ public static String capitalize(String pString) {
+ return capitalize(pString, 0);
+ }
+
+ /**
+ * Formats a number with leading zeroes, to a specified length.
+ *
+ * @param pNum The number to format
+ * @param pLen The number of digits
+ * @return A string containing the formatted number
+ * @throws IllegalArgumentException Thrown, if the number contains
+ * more digits than allowed by the length argument.
+ * @see #pad(String,int,String,boolean)
+ * @deprecated Use StringUtil.pad instead!
+ */
+
+ /*public*/
+ static String formatNumber(long pNum, int pLen) throws IllegalArgumentException {
+ StringBuilder result = new StringBuilder();
+
+ if (pNum >= Math.pow(10, pLen)) {
+ throw new IllegalArgumentException("The number to format cannot contain more digits than the length argument specifies!");
+ }
+ for (int i = pLen; i > 1; i--) {
+ if (pNum < Math.pow(10, i - 1)) {
+ result.append('0');
+ }
+ else {
+ break;
+ }
+ }
+ result.append(pNum);
+ return result.toString();
+ }
+
+ /**
+ * String length check with simple concatenation of selected pad-string.
+ * E.g. a zip number from 123 to the correct 0123.
+ *
+ * @param pSource The source string.
+ * @param pRequiredLength The accurate length of the resulting string.
+ * @param pPadString The string for concatenation.
+ * @param pPrepend The location of fill-ins, prepend (true),
+ * or append (false)
+ * @return a concatenated string.
+ * @see #cut(String,int,String)
+ */
+ // TODO: What if source is allready longer than required length?
+ // TODO: Consistency with cut
+ public static String pad(String pSource, int pRequiredLength, String pPadString, boolean pPrepend) {
+ if (pPadString == null || pPadString.length() == 0) {
+ throw new IllegalArgumentException("Pad string: \"" + pPadString + "\"");
+ }
+
+ if (pSource.length() >= pRequiredLength) {
+ return pSource;
+ }
+
+ // TODO: Benchmark the new version against the old one, to see if it's really faster
+ // Rewrite to first create pad
+ // - pad += pad; - until length is >= gap
+ // then append the pad and cut if too long
+ int gap = pRequiredLength - pSource.length();
+ StringBuilder result = new StringBuilder(pPadString);
+ while (result.length() < gap) {
+ result.append(result);
+ }
+
+ if (result.length() > gap) {
+ result.delete(gap, result.length());
+ }
+
+ return pPrepend ? result.append(pSource).toString() : result.insert(0, pSource).toString();
+
+ /*
+ StringBuilder result = new StringBuilder(pSource);
+
+ // Concatenation until proper string length
+ while (result.length() < pRequiredLength) {
+ // Prepend or append
+ if (pPrepend) { // Front
+ result.insert(0, pPadString);
+ }
+ else { // Back
+ result.append(pPadString);
+ }
+ }
+
+ // Truncate
+ if (result.length() > pRequiredLength) {
+ if (pPrepend) {
+ result.delete(0, result.length() - pRequiredLength);
+ }
+ else {
+ result.delete(pRequiredLength, result.length());
+ }
+ }
+ return result.toString();
+ */
+ }
+
+ /**
+ * Converts the string to a date, using the default date format.
+ *
+ * @param pString the string to convert
+ * @return the date
+ * @see DateFormat
+ * @see DateFormat#getInstance()
+ */
+ public static Date toDate(String pString) {
+ // Default
+ return toDate(pString, DateFormat.getInstance());
+ }
+
+ /**
+ * Converts the string to a date, using the given format.
+ *
+ * @param pString the string to convert
+ * @param pFormat the date format
+ * @return the date
+ *
+ * @see java.text.SimpleDateFormat
+ * @see java.text.SimpleDateFormat#SimpleDateFormat(String)
+ */
+ // TODO: cache formats?
+ public static Date toDate(String pString, String pFormat) {
+ // Get the format from cache, or create new and insert
+ // Return new date
+ return toDate(pString, new SimpleDateFormat(pFormat));
+ }
+
+ /**
+ * Converts the string to a date, using the given format.
+ *
+ * @param pString the string to convert
+ * @param pFormat the date format
+ * @return the date
+ * @see SimpleDateFormat
+ * @see SimpleDateFormat#SimpleDateFormat(String)
+ * @see DateFormat
+ */
+ public static Date toDate(final String pString, final DateFormat pFormat) {
+ try {
+ synchronized (pFormat) {
+ // Parse date using given format
+ return pFormat.parse(pString);
+ }
+ }
+ catch (ParseException pe) {
+ // Wrap in RuntimeException
+ throw new IllegalArgumentException(pe.getMessage());
+ }
+ }
+
+ /**
+ * Converts the string to a jdbc Timestamp, using the standard Timestamp
+ * escape format.
+ *
+ * @param pValue the value
+ * @return a new {@code Timestamp}
+ * @see java.sql.Timestamp
+ * @see java.sql.Timestamp#valueOf(String)
+ */
+ public static Timestamp toTimestamp(final String pValue) {
+ // Parse date using default format
+ return Timestamp.valueOf(pValue);
+ }
+
+ /**
+ * Converts a delimiter separated String to an array of Strings.
+ *
+ * @param pString The comma-separated string
+ * @param pDelimiters The delimiter string
+ * @return a {@code String} array containing the delimiter separated elements
+ */
+ public static String[] toStringArray(String pString, String pDelimiters) {
+ if (isEmpty(pString)) {
+ return new String[0];
+ }
+
+ StringTokenIterator st = new StringTokenIterator(pString, pDelimiters);
+ List v = new ArrayList();
+
+ while (st.hasMoreElements()) {
+ v.add(st.nextToken());
+ }
+
+ return v.toArray(new String[v.size()]);
+ }
+
+ /**
+ * Converts a comma-separated String to an array of Strings.
+ *
+ * @param pString The comma-separated string
+ * @return a {@code String} array containing the comma-separated elements
+ * @see #toStringArray(String,String)
+ */
+ public static String[] toStringArray(String pString) {
+ return toStringArray(pString, DELIMITER_STRING);
+ }
+
+ /**
+ * Converts a comma-separated String to an array of ints.
+ *
+ * @param pString The comma-separated string
+ * @param pDelimiters The delimiter string
+ * @param pBase The radix
+ * @return an {@code int} array
+ * @throws NumberFormatException if any of the elements are not parseable
+ * as an int
+ */
+ public static int[] toIntArray(String pString, String pDelimiters, int pBase) {
+ if (isEmpty(pString)) {
+ return new int[0];
+ }
+
+ // Some room for improvement here...
+ String[] temp = toStringArray(pString, pDelimiters);
+ int[] array = new int[temp.length];
+
+ for (int i = 0; i < array.length; i++) {
+ array[i] = Integer.parseInt(temp[i], pBase);
+ }
+ return array;
+ }
+
+ /**
+ * Converts a comma-separated String to an array of ints.
+ *
+ * @param pString The comma-separated string
+ * @return an {@code int} array
+ * @throws NumberFormatException if any of the elements are not parseable
+ * as an int
+ * @see #toStringArray(String,String)
+ * @see #DELIMITER_STRING
+ */
+ public static int[] toIntArray(String pString) {
+ return toIntArray(pString, DELIMITER_STRING, 10);
+ }
+
+ /**
+ * Converts a comma-separated String to an array of ints.
+ *
+ * @param pString The comma-separated string
+ * @param pDelimiters The delimiter string
+ * @return an {@code int} array
+ * @throws NumberFormatException if any of the elements are not parseable
+ * as an int
+ * @see #toIntArray(String,String)
+ */
+ public static int[] toIntArray(String pString, String pDelimiters) {
+ return toIntArray(pString, pDelimiters, 10);
+ }
+
+ /**
+ * Converts a comma-separated String to an array of longs.
+ *
+ * @param pString The comma-separated string
+ * @param pDelimiters The delimiter string
+ * @return a {@code long} array
+ * @throws NumberFormatException if any of the elements are not parseable
+ * as a long
+ */
+ public static long[] toLongArray(String pString, String pDelimiters) {
+ if (isEmpty(pString)) {
+ return new long[0];
+ }
+
+ // Some room for improvement here...
+ String[] temp = toStringArray(pString, pDelimiters);
+ long[] array = new long[temp.length];
+
+ for (int i = 0; i < array.length; i++) {
+ array[i] = Long.parseLong(temp[i]);
+ }
+ return array;
+ }
+
+ /**
+ * Converts a comma-separated String to an array of longs.
+ *
+ * @param pString The comma-separated string
+ * @return a {@code long} array
+ * @throws NumberFormatException if any of the elements are not parseable
+ * as a long
+ * @see #toStringArray(String,String)
+ * @see #DELIMITER_STRING
+ */
+ public static long[] toLongArray(String pString) {
+ return toLongArray(pString, DELIMITER_STRING);
+ }
+
+ /**
+ * Converts a comma-separated String to an array of doubles.
+ *
+ * @param pString The comma-separated string
+ * @param pDelimiters The delimiter string
+ * @return a {@code double} array
+ * @throws NumberFormatException if any of the elements are not parseable
+ * as a double
+ */
+ public static double[] toDoubleArray(String pString, String pDelimiters) {
+ if (isEmpty(pString)) {
+ return new double[0];
+ }
+
+ // Some room for improvement here...
+ String[] temp = toStringArray(pString, pDelimiters);
+ double[] array = new double[temp.length];
+
+ for (int i = 0; i < array.length; i++) {
+ array[i] = Double.valueOf(temp[i]);
+
+ // Double.parseDouble() is 1.2...
+ }
+ return array;
+ }
+
+ /**
+ * Converts a comma-separated String to an array of doubles.
+ *
+ * @param pString The comma-separated string
+ * @return a {@code double} array
+ * @throws NumberFormatException if any of the elements are not parseable
+ * as a double
+ * @see #toDoubleArray(String,String)
+ * @see #DELIMITER_STRING
+ */
+ public static double[] toDoubleArray(String pString) {
+ return toDoubleArray(pString, DELIMITER_STRING);
+ }
+
+ /**
+ * Parses a string to a Color.
+ * The argument can be a color constant (static constant from
+ * {@link java.awt.Color java.awt.Color}), like {@code black} or
+ * {@code red}, or it can be HTML/CSS-style, on the format:
+ *
+ * - {@code #RRGGBB}, where RR, GG and BB means two digit
+ * hexadecimal for red, green and blue values respectively.
+ * - {@code #AARRGGBB}, as above, with AA as alpha component.
+ * - {@code #RGB}, where R, G and B means one digit
+ * hexadecimal for red, green and blue values respectively.
+ * - {@code #ARGB}, as above, with A as alpha component.
+ *
+ *
+ * @param pString the string representation of the color
+ * @return the {@code Color} object, or {@code null} if the argument
+ * is {@code null}
+ * @throws IllegalArgumentException if the string does not map to a color.
+ * @see java.awt.Color
+ */
+ public static Color toColor(String pString) {
+ // No string, no color
+ if (pString == null) {
+ return null;
+ }
+
+ // #RRGGBB format
+ if (pString.charAt(0) == '#') {
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ int a = -1;// alpha
+
+ if (pString.length() >= 7) {
+ int idx = 1;
+
+ // AA
+ if (pString.length() >= 9) {
+ a = Integer.parseInt(pString.substring(idx, idx + 2), 0x10);
+ idx += 2;
+ }
+ // RR GG BB
+ r = Integer.parseInt(pString.substring(idx, idx + 2), 0x10);
+ g = Integer.parseInt(pString.substring(idx + 2, idx + 4), 0x10);
+ b = Integer.parseInt(pString.substring(idx + 4, idx + 6), 0x10);
+
+ }
+ else if (pString.length() >= 4) {
+ int idx = 1;
+
+ // A
+ if (pString.length() >= 5) {
+ a = Integer.parseInt(pString.substring(idx, ++idx), 0x10) * 0x10;
+ }
+ // R G B
+ r = Integer.parseInt(pString.substring(idx, ++idx), 0x10) * 0x10;
+ g = Integer.parseInt(pString.substring(idx, ++idx), 0x10) * 0x10;
+ b = Integer.parseInt(pString.substring(idx, ++idx), 0x10) * 0x10;
+ }
+ if (a != -1) {
+ // With alpha
+ return new Color(r, g, b, a);
+ }
+
+ // No alpha
+ return new Color(r, g, b);
+ }
+
+ // Get color by name
+ try {
+ Class colorClass = Color.class;
+ Field field = null;
+
+ // Workaround for stupidity in Color class constant field names
+ try {
+ field = colorClass.getField(pString);
+ }
+ catch (Exception e) {
+ // Don't care, this is just a workaround...
+ }
+ if (field == null) {
+ // NOTE: The toLowerCase() on the next line will lose darkGray
+ // and lightGray...
+ field = colorClass.getField(pString.toLowerCase());
+ }
+
+ // Only try to get public final fields
+ int mod = field.getModifiers();
+
+ if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) {
+ return (Color) field.get(null);
+ }
+ }
+ catch (NoSuchFieldException nsfe) {
+ // No such color, throw illegal argument?
+ throw new IllegalArgumentException("No such color: " + pString);
+ }
+ catch (SecurityException se) {
+ // Can't access field, return null
+ }
+ catch (IllegalAccessException iae) {
+ // Can't happen, as the field must be public static
+ }
+ catch (IllegalArgumentException iar) {
+ // Can't happen, as the field must be static
+ }
+
+ // This should never be reached, but you never know... ;-)
+ return null;
+ }
+
+ /**
+ * Creates a HTML/CSS String representation of the given color.
+ * The HTML/CSS color format is defined as:
+ *
+ * - {@code #RRGGBB}, where RR, GG and BB means two digit
+ * hexadecimal for red, green and blue values respectively.
+ * - {@code #AARRGGBB}, as above, with AA as alpha component.
+ *
+ *
+ * Examlples: {@code toColorString(Color.red) == "#ff0000"},
+ * {@code toColorString(new Color(0xcc, 0xcc, 0xcc)) == "#cccccc"}.
+ *
+ *
+ * @param pColor the color
+ * @return A String representation of the color on HTML/CSS form
+ */
+ // TODO: Consider moving to ImageUtil?
+ public static String toColorString(Color pColor) {
+ // Not a color...
+ if (pColor == null) {
+ return null;
+ }
+
+ StringBuilder str = new StringBuilder(Integer.toHexString(pColor.getRGB()));
+
+ // Make sure string is 8 chars
+ for (int i = str.length(); i < 8; i++) {
+ str.insert(0, '0');
+ }
+
+ // All opaque is default
+ if (str.charAt(0) == 'f' && str.charAt(1) == 'f') {
+ str.delete(0, 2);
+ }
+
+ // Prepend hash
+ return str.insert(0, '#').toString();
+ }
+
+ /**
+ * Tests a string, to see if it is an number (element of Z).
+ * Valid integers are positive natural numbers (1, 2, 3, ...),
+ * their negatives (?1, ?2, ?3, ...) and the number zero.
+ *
+ * Note that there is no guarantees made, that this number can be
+ * represented as either an int or a long.
+ *
+ *
+ * @param pString The string to check.
+ * @return true if the String is a natural number.
+ */
+ public static boolean isNumber(String pString) {
+ if (isEmpty(pString)) {
+ return false;
+ }
+
+ // Special case for first char, may be minus sign ('-')
+ char ch = pString.charAt(0);
+ if (!(ch == '-' || Character.isDigit(ch))) {
+ return false;
+ }
+
+ // Test every char
+ for (int i = 1; i < pString.length(); i++) {
+ if (!Character.isDigit(pString.charAt(i))) {
+ return false;
+ }
+ }
+
+ // All digits must be a natural number
+ return true;
+ }
+
+ /*
+ * This version is benchmarked against toStringArray and found to be
+ * increasingly slower, the more elements the string contains.
+ * Kept here
+ */
+
+ /**
+ * Removes all occurences of a specific character in a string.
+ * This method is not design for efficiency!
+ *
+ *
+ * @param pSource
+ * @param pSubstring
+ * @param pPosition
+ * @return the modified string.
+ */
+
+ /*
+ public static String removeChar(String pSourceString, final char pBadChar) {
+
+ char[] sourceCharArray = pSourceString.toCharArray();
+ List modifiedCharList = new Vector(sourceCharArray.length, 1);
+
+ // Filter the string
+ for (int i = 0; i < sourceCharArray.length; i++) {
+ if (sourceCharArray[i] != pBadChar) {
+ modifiedCharList.add(new Character(sourceCharArray[i]));
+ }
+ }
+
+ // Clean the character list
+ modifiedCharList = (List) CollectionUtil.purifyCollection((Collection) modifiedCharList);
+
+ // Create new modified String
+ char[] modifiedCharArray = new char[modifiedCharList.size()];
+ for (int i = 0; i < modifiedCharArray.length; i++) {
+ modifiedCharArray[i] = ((Character) modifiedCharList.get(i)).charValue();
+ }
+
+ return new String(modifiedCharArray);
+ }
+ */
+
+ /**
+ *
+ * This method is not design for efficiency!
+ *
+ * @param pSourceString The String for modification.
+ * @param pBadChars The char array containing the characters to remove from the source string.
+ * @return the modified string.
+ * @-deprecated Not tested yet!
+ *
+ */
+
+ /*
+ public static String removeChars(String pSourceString, final char[] pBadChars) {
+
+ char[] sourceCharArray = pSourceString.toCharArray();
+ List modifiedCharList = new Vector(sourceCharArray.length, 1);
+
+ Map badCharMap = new Hashtable();
+ Character dummyChar = new Character('*');
+ for (int i = 0; i < pBadChars.length; i++) {
+ badCharMap.put(new Character(pBadChars[i]), dummyChar);
+ }
+
+ // Filter the string
+ for (int i = 0; i < sourceCharArray.length; i++) {
+ Character arrayChar = new Character(sourceCharArray[i]);
+ if (!badCharMap.containsKey(arrayChar)) {
+ modifiedCharList.add(new Character(sourceCharArray[i]));
+ }
+ }
+
+ // Clean the character list
+ modifiedCharList = (List) CollectionUtil.purifyCollection((Collection) modifiedCharList);
+
+ // Create new modified String
+ char[] modifiedCharArray = new char[modifiedCharList.size()];
+ for (int i = 0; i < modifiedCharArray.length; i++) {
+ modifiedCharArray[i] = ((Character) modifiedCharList.get(i)).charValue();
+ }
+
+ return new String(modifiedCharArray);
+
+ }
+ */
+
+ /**
+ * Ensures that a string includes a given substring at a given position.
+ *
+ * Extends the string with a given string if it is not already there.
+ * E.g an URL "www.vg.no", to "http://www.vg.no".
+ *
+ *
+ * @param pSource The source string.
+ * @param pSubstring The substring to include.
+ * @param pPosition The location of the fill-in, the index starts with 0.
+ * @return the string, with the substring at the given location.
+ */
+ static String ensureIncludesAt(String pSource, String pSubstring, int pPosition) {
+ StringBuilder newString = new StringBuilder(pSource);
+
+ try {
+ String existingSubstring = pSource.substring(pPosition, pPosition + pSubstring.length());
+
+ if (!existingSubstring.equalsIgnoreCase(pSubstring)) {
+ newString.insert(pPosition, pSubstring);
+ }
+ }
+ catch (Exception e) {
+ // Do something!?
+ }
+ return newString.toString();
+ }
+
+ /**
+ * Ensures that a string does not include a given substring at a given
+ * position.
+ *
+ * Removes a given substring from a string if it is there.
+ * E.g an URL "http://www.vg.no", to "www.vg.no".
+ *
+ *
+ * @param pSource The source string.
+ * @param pSubstring The substring to check and possibly remove.
+ * @param pPosition The location of possible substring removal, the index starts with 0.
+ * @return the string, without the substring at the given location.
+ */
+ static String ensureExcludesAt(String pSource, String pSubstring, int pPosition) {
+ StringBuilder newString = new StringBuilder(pSource);
+
+ try {
+ String existingString = pSource.substring(pPosition + 1, pPosition + pSubstring.length() + 1);
+
+ if (!existingString.equalsIgnoreCase(pSubstring)) {
+ newString.delete(pPosition, pPosition + pSubstring.length());
+ }
+ }
+ catch (Exception e) {
+ // Do something!?
+ }
+ return newString.toString();
+ }
+
+ /**
+ * Gets the first substring between the given string boundaries.
+ *
+ * @param pSource The source string.
+ * @param pBeginBoundaryString The string that marks the beginning.
+ * @param pEndBoundaryString The string that marks the end.
+ * @param pOffset The index to start searching in the source
+ * string. If it is less than 0, the index will be set to 0.
+ * @return the substring demarcated by the given string boundaries or null
+ * if not both string boundaries are found.
+ */
+ public static String substring(final String pSource, final String pBeginBoundaryString, final String pEndBoundaryString,
+ final int pOffset) {
+ // Check offset
+ int offset = (pOffset < 0)
+ ? 0
+ : pOffset;
+
+ // Find the start index
+ int startIndex = pSource.indexOf(pBeginBoundaryString, offset) + pBeginBoundaryString.length();
+
+ if (startIndex < 0) {
+ return null;
+ }
+
+ // Find the end index
+ int endIndex = pSource.indexOf(pEndBoundaryString, startIndex);
+
+ if (endIndex < 0) {
+ return null;
+ }
+ return pSource.substring(startIndex, endIndex);
+ }
+
+ /**
+ * Removes the first substring demarcated by the given string boundaries.
+ *
+ * @param pSource The source string.
+ * @param pBeginBoundaryChar The character that marks the beginning of the
+ * unwanted substring.
+ * @param pEndBoundaryChar The character that marks the end of the
+ * unwanted substring.
+ * @param pOffset The index to start searching in the source
+ * string. If it is less than 0, the index will be set to 0.
+ * @return the source string with all the demarcated substrings removed,
+ * included the demarcation characters.
+ * @deprecated this method actually removes all demarcated substring.. doesn't it?
+ */
+
+ /*public*/
+ static String removeSubstring(final String pSource, final char pBeginBoundaryChar, final char pEndBoundaryChar, final int pOffset) {
+ StringBuilder filteredString = new StringBuilder();
+ boolean insideDemarcatedArea = false;
+ char[] charArray = pSource.toCharArray();
+
+ for (char c : charArray) {
+ if (!insideDemarcatedArea) {
+ if (c == pBeginBoundaryChar) {
+ insideDemarcatedArea = true;
+ }
+ else {
+ filteredString.append(c);
+ }
+ }
+ else {
+ if (c == pEndBoundaryChar) {
+ insideDemarcatedArea = false;
+ }
+ }
+ }
+ return filteredString.toString();
+ }
+
+ /**
+ * Removes all substrings demarcated by the given string boundaries.
+ *
+ * @param pSource The source string.
+ * @param pBeginBoundaryChar The character that marks the beginning of the unwanted substring.
+ * @param pEndBoundaryChar The character that marks the end of the unwanted substring.
+ * @return the source string with all the demarcated substrings removed, included the demarcation characters.
+ */
+ /*public*/
+ static String removeSubstrings(final String pSource, final char pBeginBoundaryChar, final char pEndBoundaryChar) {
+ StringBuilder filteredString = new StringBuilder();
+ boolean insideDemarcatedArea = false;
+ char[] charArray = pSource.toCharArray();
+
+ for (char c : charArray) {
+ if (!insideDemarcatedArea) {
+ if (c == pBeginBoundaryChar) {
+ insideDemarcatedArea = true;
+ }
+ else {
+ filteredString.append(c);
+ }
+ }
+ else {
+ if (c == pEndBoundaryChar) {
+ insideDemarcatedArea = false;
+ }
+ }
+ }
+ return filteredString.toString();
+ }
+
+ /**
+ * Gets the first element of a {@code String} containing string elements delimited by a given delimiter.
+ * NB - Straightforward implementation!
+ *
+ * @param pSource The source string.
+ * @param pDelimiter The delimiter used in the source string.
+ * @return The last string element.
+ */
+ // TODO: This method should be re-implemented for more efficient execution.
+ public static String getFirstElement(final String pSource, final String pDelimiter) {
+ if (pDelimiter == null) {
+ throw new IllegalArgumentException("delimiter == null");
+ }
+
+ if (StringUtil.isEmpty(pSource)) {
+ return pSource;
+ }
+
+ int idx = pSource.indexOf(pDelimiter);
+ if (idx >= 0) {
+ return pSource.substring(0, idx);
+ }
+ return pSource;
+ }
+
+ /**
+ * Gets the last element of a {@code String} containing string elements
+ * delimited by a given delimiter.
+ * NB - Straightforward implementation!
+ *
+ * @param pSource The source string.
+ * @param pDelimiter The delimiter used in the source string.
+ * @return The last string element.
+ */
+ public static String getLastElement(final String pSource, final String pDelimiter) {
+ if (pDelimiter == null) {
+ throw new IllegalArgumentException("delimiter == null");
+ }
+
+ if (StringUtil.isEmpty(pSource)) {
+ return pSource;
+ }
+ int idx = pSource.lastIndexOf(pDelimiter);
+ if (idx >= 0) {
+ return pSource.substring(idx + 1);
+ }
+ return pSource;
+ }
+
+ /**
+ * Converts a string array to a string of comma-separated values.
+ *
+ * @param pStringArray the string array
+ * @return A string of comma-separated values
+ */
+ public static String toCSVString(Object[] pStringArray) {
+ return toCSVString(pStringArray, ", ");
+ }
+
+ /**
+ * Converts a string array to a string separated by the given delimiter.
+ *
+ * @param pStringArray the string array
+ * @param pDelimiterString the delimiter string
+ * @return string of delimiter separated values
+ * @throws IllegalArgumentException if {@code pDelimiterString == null}
+ */
+ public static String toCSVString(Object[] pStringArray, String pDelimiterString) {
+ if (pStringArray == null) {
+ return "";
+ }
+ if (pDelimiterString == null) {
+ throw new IllegalArgumentException("delimiter == null");
+ }
+
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < pStringArray.length; i++) {
+ if (i > 0) {
+ buffer.append(pDelimiterString);
+ }
+
+ buffer.append(pStringArray[i]);
+ }
+
+ return buffer.toString();
+ }
+
+ /**
+ * @param pObject the object
+ * @return a deep string representation of the given object
+ */
+ public static String deepToString(Object pObject) {
+ return deepToString(pObject, false, 1);
+ }
+
+ /**
+ * @param pObject the object
+ * @param pDepth the maximum depth
+ * @param pForceDeep {@code true} to force deep {@code toString}, even
+ * if object overrides toString
+ * @return a deep string representation of the given object
+ */
+ // TODO: Array handling (print full type and length)
+ // TODO: Register handlers for specific toDebugString handling? :-)
+ public static String deepToString(Object pObject, boolean pForceDeep, int pDepth) {
+ // Null is null
+ if (pObject == null) {
+ return null;
+ }
+
+ // Implements toString, use it as-is unless pForceDeep
+ if (!pForceDeep && !isIdentityToString(pObject)) {
+ return pObject.toString();
+ }
+
+ StringBuilder buffer = new StringBuilder();
+
+ if (pObject.getClass().isArray()) {
+ // Special array handling
+ Class componentClass = pObject.getClass();
+ while (componentClass.isArray()) {
+ buffer.append('[');
+ buffer.append(Array.getLength(pObject));
+ buffer.append(']');
+ componentClass = componentClass.getComponentType();
+ }
+ buffer.insert(0, componentClass);
+ buffer.append(" {hashCode=");
+ buffer.append(Integer.toHexString(pObject.hashCode()));
+ buffer.append("}");
+ }
+ else {
+ // Append toString value only if overridden
+ if (isIdentityToString(pObject)) {
+ buffer.append(" {");
+ }
+ else {
+ buffer.append(" {toString=");
+ buffer.append(pObject.toString());
+ buffer.append(", ");
+ }
+ buffer.append("hashCode=");
+ buffer.append(Integer.toHexString(pObject.hashCode()));
+ // Loop through, and filter out any getters
+ Method[] methods = pObject.getClass().getMethods();
+ for (Method method : methods) {
+ // Filter only public methods
+ if (Modifier.isPublic(method.getModifiers())) {
+ String methodName = method.getName();
+
+ // Find name of property
+ String name = null;
+ if (!methodName.equals("getClass")
+ && methodName.length() > 3 && methodName.startsWith("get")
+ && Character.isUpperCase(methodName.charAt(3))) {
+ name = methodName.substring(3);
+ }
+ else if (methodName.length() > 2 && methodName.startsWith("is")
+ && Character.isUpperCase(methodName.charAt(2))) {
+ name = methodName.substring(2);
+ }
+
+ if (name != null) {
+ // If lowercase name, convert, else keep case
+ if (name.length() > 1 && Character.isLowerCase(name.charAt(1))) {
+ name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
+ }
+
+ Class[] paramTypes = method.getParameterTypes();// involves array copying...
+ boolean hasParams = (paramTypes != null && paramTypes.length > 0);
+ boolean isVoid = Void.TYPE.equals(method.getReturnType());
+
+ // Filter return type & parameters
+ if (!isVoid && !hasParams) {
+ try {
+ Object value = method.invoke(pObject);
+ buffer.append(", ");
+ buffer.append(name);
+ buffer.append('=');
+ if (pDepth != 0 && value != null && isIdentityToString(value)) {
+ buffer.append(deepToString(value, pForceDeep, pDepth > 0 ? pDepth - 1 : -1));
+ }
+ else {
+ buffer.append(value);
+ }
+ }
+ catch (Exception e) {
+ // Next..!
+ }
+ }
+ }
+ }
+ }
+ buffer.append('}');
+
+ // Get toString from original object
+ buffer.insert(0, pObject.getClass().getName());
+ }
+
+ return buffer.toString();
+ }
+
+ /**
+ * Tests if the {@code toString} method of the given object is inherited
+ * from {@code Object}.
+ *
+ * @param pObject the object
+ * @return {@code true} if toString of class Object
+ */
+ private static boolean isIdentityToString(Object pObject) {
+ try {
+ Method toString = pObject.getClass().getMethod("toString");
+ if (toString.getDeclaringClass() == Object.class) {
+ return true;
+ }
+ }
+ catch (Exception ignore) {
+ // Ignore
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a string on the same format as {@code Object.toString()}.
+ *
+ * @param pObject the object
+ * @return the object as a {@code String} on the format of
+ * {@code Object.toString()}
+ */
+ public static String identityToString(Object pObject) {
+ if (pObject == null) {
+ return null;
+ }
+ else {
+ return pObject.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(pObject));
+ }
+ }
+
+ /**
+ * Tells whether or not the given string string matches the given regular
+ * expression.
+ *
+ * An invocation of this method of the form
+ * matches(str, regex) yields exactly the
+ * same result as the expression
+ *
+ * {@link Pattern}.
+ * {@link Pattern#matches(String, CharSequence) matches}
+ * (regex, str)
+ *
+ * @param pString the string
+ * @param pRegex the regular expression to which this string is to be matched
+ * @return {@code true} if, and only if, this string matches the
+ * given regular expression
+ * @throws PatternSyntaxException if the regular expression's syntax is invalid
+ * @see Pattern
+ * @see String#matches(String)
+ */
+ public boolean matches(String pString, String pRegex) throws PatternSyntaxException {
+ return Pattern.matches(pRegex, pString);
+ }
+
+ /**
+ * Replaces the first substring of the given string that matches the given
+ * regular expression with the given pReplacement.
+ *
+ * An invocation of this method of the form
+ *
+ * replaceFirst(str, regex, repl)
+ *
+ * yields exactly the same result as the expression:
+ *
+ *
+ * {@link Pattern}.{@link Pattern#compile(String) compile}(regex).
+ * {@link Pattern#matcher matcher}(str).
+ * {@link java.util.regex.Matcher#replaceFirst replaceFirst}(repl)
+ *
+ *
+ * @param pString the string
+ * @param pRegex the regular expression to which this string is to be matched
+ * @param pReplacement the replacement text
+ * @return The resulting {@code String}
+ * @throws PatternSyntaxException if the regular expression's syntax is invalid
+ * @see Pattern
+ * @see java.util.regex.Matcher#replaceFirst(String)
+ */
+ public String replaceFirst(String pString, String pRegex, String pReplacement) {
+ return Pattern.compile(pRegex).matcher(pString).replaceFirst(pReplacement);
+ }
+
+ /**
+ * Replaces each substring of this string that matches the given
+ * regular expression with the given pReplacement.
+ *
+ * An invocation of this method of the form
+ * replaceAll(str, pRegex, repl)
+ * yields exactly the same result as the expression
+ *
+ *
+ * {@link Pattern}.{@link Pattern#compile(String) compile}(pRegex).
+ * {@link Pattern#matcher matcher}(str{@code ).
+ * {@link java.util.regex.Matcher#replaceAll replaceAll}(}repl{@code )}
+ *
+ *
+ * @param pString the string
+ * @param pRegex the regular expression to which this string is to be matched
+ * @param pReplacement the replacement string
+ * @return The resulting {@code String}
+ * @throws PatternSyntaxException if the regular expression's syntax is invalid
+ * @see Pattern
+ * @see String#replaceAll(String,String)
+ */
+ public String replaceAll(String pString, String pRegex, String pReplacement) {
+ return Pattern.compile(pRegex).matcher(pString).replaceAll(pReplacement);
+ }
+
+ /**
+ * Splits this string around matches of the given regular expression.
+ *
+ * The array returned by this method contains each substring of this
+ * string that is terminated by another substring that matches the given
+ * expression or is terminated by the end of the string. The substrings in
+ * the array are in the order in which they occur in this string. If the
+ * expression does not match any part of the input then the resulting array
+ * has just one element, namely this string.
+ *
+ *
+ * The {@code pLimit} parameter controls the number of times the
+ * pattern is applied and therefore affects the length of the resulting
+ * array. If the pLimit n is greater than zero then the pattern
+ * will be applied at most n - 1 times, the array's
+ * length will be no greater than n, and the array's last entry
+ * will contain all input beyond the last matched delimiter. If n
+ * is non-positive then the pattern will be applied as many times as
+ * possible and the array can have any length. If n is zero then
+ * the pattern will be applied as many times as possible, the array can
+ * have any length, and trailing empty strings will be discarded.
+ *
+ *
+ * An invocation of this method of the form
+ * split(str, regex, n)
+ * yields the same result as the expression:
+ *
+ * {@link Pattern}.
+ * {@link Pattern#compile(String) compile}(regex).
+ * {@link Pattern#split(CharSequence,int) split}(str, n)
+ *
+ *
+ * @param pString the string
+ * @param pRegex the delimiting regular expression
+ * @param pLimit the result threshold, as described above
+ * @return the array of strings computed by splitting this string
+ * around matches of the given regular expression
+ * @throws PatternSyntaxException
+ * if the regular expression's syntax is invalid
+ * @see Pattern
+ * @see String#split(String,int)
+ */
+ public String[] split(String pString, String pRegex, int pLimit) {
+ return Pattern.compile(pRegex).split(pString, pLimit);
+ }
+
+ /**
+ * Splits this string around matches of the given regular expression.
+ *
+ * This method works as if by invoking the two-argument
+ * {@link #split(String,String,int) split} method with the given
+ * expression and a limit argument of zero.
+ * Trailing empty strings are therefore not included in the resulting array.
+ *
+ *
+ * @param pString the string
+ * @param pRegex the delimiting regular expression
+ * @return the array of strings computed by splitting this string
+ * around matches of the given regular expression
+ * @throws PatternSyntaxException if the regular expression's syntax is invalid
+ * @see Pattern
+ * @see String#split(String)
+ */
+ public String[] split(String pString, String pRegex) {
+ return split(pString, pRegex, 0);
+ }
+
+ /**
+ * Converts the input string
+ * from camel-style (Java in-fix) naming convention
+ * to Lisp-style naming convention (hyphen delimitted, all lower case).
+ * Other characters in the string are left untouched.
+ *
+ * Eg.
+ * {@code "foo" => "foo"},
+ * {@code "fooBar" => "foo-bar"},
+ * {@code "myURL" => "my-url"},
+ * {@code "HttpRequestWrapper" => "http-request-wrapper"}
+ * {@code "HttpURLConnection" => "http-url-connection"}
+ * {@code "my45Caliber" => "my-45-caliber"}
+ * {@code "allready-lisp" => "allready-lisp"}
+ *
+ *
+ * @param pString the camel-style input string
+ * @return the string converted to lisp-style naming convention
+ * @throws IllegalArgumentException if {@code pString == null}
+ * @see #lispToCamel(String)
+ */
+ // TODO: RefactorMe!
+ public static String camelToLisp(final String pString) {
+ if (pString == null) {
+ throw new IllegalArgumentException("string == null");
+ }
+ if (pString.length() == 0) {
+ return pString;
+ }
+
+ StringBuilder buf = null;
+ int lastPos = 0;
+ boolean inCharSequence = false;
+ boolean inNumberSequence = false;
+
+ // NOTE: Start at index 1, as first letter should never be hyphen
+ for (int i = 1; i < pString.length(); i++) {
+ char current = pString.charAt(i);
+ if (Character.isUpperCase(current)) {
+ // Init buffer if necessary
+ if (buf == null) {
+ buf = new StringBuilder(pString.length() + 3);// Allow for some growth
+ }
+
+ if (inNumberSequence) {
+ // Sequence end
+ inNumberSequence = false;
+
+ buf.append(pString.substring(lastPos, i));
+ if (current != '-') {
+ buf.append('-');
+ }
+ lastPos = i;
+ continue;
+ }
+
+ // Treat multiple uppercase chars as single word
+ char previous = pString.charAt(i - 1);
+ if (i == lastPos || Character.isUpperCase(previous)) {
+ inCharSequence = true;
+ continue;
+ }
+
+ // Append word
+ buf.append(pString.substring(lastPos, i).toLowerCase());
+ if (previous != '-') {
+ buf.append('-');
+ }
+ buf.append(Character.toLowerCase(current));
+
+ lastPos = i + 1;
+ }
+ else if (Character.isDigit(current)) {
+ // Init buffer if necessary
+ if (buf == null) {
+ buf = new StringBuilder(pString.length() + 3);// Allow for some growth
+ }
+
+ if (inCharSequence) {
+ // Sequence end
+ inCharSequence = false;
+
+ buf.append(pString.substring(lastPos, i).toLowerCase());
+ if (current != '-') {
+ buf.append('-');
+ }
+ lastPos = i;
+ continue;
+ }
+
+ // Treat multiple digits as single word
+ char previous = pString.charAt(i - 1);
+ if (i == lastPos || Character.isDigit(previous)) {
+ inNumberSequence = true;
+ continue;
+ }
+
+ // Append word
+ buf.append(pString.substring(lastPos, i).toLowerCase());
+ if (previous != '-') {
+ buf.append('-');
+ }
+ buf.append(Character.toLowerCase(current));
+
+ lastPos = i + 1;
+ }
+ else if (inNumberSequence) {
+ // Sequence end
+ inNumberSequence = false;
+
+ buf.append(pString.substring(lastPos, i));
+ if (current != '-') {
+ buf.append('-');
+ }
+ lastPos = i;
+ }
+ else if (inCharSequence) {
+ // Sequence end
+ inCharSequence = false;
+
+ // NOTE: Special treatment! Last upper case, is first char in
+ // next word, not last char in this word
+ buf.append(pString.substring(lastPos, i - 1).toLowerCase());
+ if (current != '-') {
+ buf.append('-');
+ }
+ lastPos = i - 1;
+ }
+ }
+
+ if (buf != null) {
+ // Append the rest
+ buf.append(pString.substring(lastPos).toLowerCase());
+ return buf.toString();
+ }
+ else {
+ return Character.isUpperCase(pString.charAt(0)) ? pString.toLowerCase() : pString;
+ }
+ }
+
+ /**
+ * Converts the input string
+ * from Lisp-style naming convention (hyphen delimitted, all lower case)
+ * to camel-style (Java in-fix) naming convention.
+ * Other characters in the string are left untouched.
+ *
+ * Eg.
+ * {@code "foo" => "foo"},
+ * {@code "foo-bar" => "fooBar"},
+ * {@code "http-request-wrapper" => "httpRequestWrapper"}
+ * {@code "my-45-caliber" => "my45Caliber"}
+ * {@code "allreadyCamel" => "allreadyCamel"}
+ *
+ *
+ * @param pString the lisp-style input string
+ * @return the string converted to camel-style
+ * @throws IllegalArgumentException if {@code pString == null}
+ * @see #lispToCamel(String,boolean)
+ * @see #camelToLisp(String)
+ */
+ public static String lispToCamel(final String pString) {
+ return lispToCamel(pString, false);
+ }
+
+ /**
+ * Converts the input string
+ * from Lisp-style naming convention (hyphen delimitted, all lower case)
+ * to camel-style (Java in-fix) naming convention.
+ * Other characters in the string are left untouched.
+ *
+ * To create a string starting with a lower case letter
+ * (like Java variable names, etc),
+ * specify the {@code pFirstUpperCase} paramter to be {@code false}.
+ * Eg.
+ * {@code "foo" => "foo"},
+ * {@code "foo-bar" => "fooBar"},
+ * {@code "allreadyCamel" => "allreadyCamel"}
+ *
+ *
+ * To create a string starting with an upper case letter
+ * (like Java class name, etc),
+ * specify the {@code pFirstUpperCase} paramter to be {@code true}.
+ * Eg.
+ * {@code "http-request-wrapper" => "HttpRequestWrapper"}
+ * {@code "my-12-monkeys" => "My12Monkeys"}
+ *
+ *
+ * @param pString the lisp-style input string
+ * @param pFirstUpperCase {@code true} if the first char should be
+ * upper case
+ * @return the string converted to camel-style
+ * @throws IllegalArgumentException if {@code pString == null}
+ * @see #camelToLisp(String)
+ */
+ public static String lispToCamel(final String pString, final boolean pFirstUpperCase) {
+ if (pString == null) {
+ throw new IllegalArgumentException("string == null");
+ }
+ if (pString.length() == 0) {
+ return pString;
+ }
+
+ StringBuilder buf = null;
+ int lastPos = 0;
+
+ for (int i = 0; i < pString.length(); i++) {
+ char current = pString.charAt(i);
+ if (current == '-') {
+
+ // Init buffer if necessary
+ if (buf == null) {
+ buf = new StringBuilder(pString.length() - 1);// Can't be larger
+ }
+
+ // Append with upper case
+ if (lastPos != 0 || pFirstUpperCase) {
+ buf.append(Character.toUpperCase(pString.charAt(lastPos)));
+ lastPos++;
+ }
+
+ buf.append(pString.substring(lastPos, i).toLowerCase());
+ lastPos = i + 1;
+ }
+ }
+
+ if (buf != null) {
+ buf.append(Character.toUpperCase(pString.charAt(lastPos)));
+ buf.append(pString.substring(lastPos + 1).toLowerCase());
+ return buf.toString();
+ }
+ else {
+ if (pFirstUpperCase && !Character.isUpperCase(pString.charAt(0))) {
+ return capitalize(pString, 0);
+ }
+ else
+ if (!pFirstUpperCase && Character.isUpperCase(pString.charAt(0))) {
+ return Character.toLowerCase(pString.charAt(0)) + pString.substring(1);
+ }
+
+ return pString;
+ }
+ }
+
+ public static String reverse(final String pString) {
+ final char[] chars = new char[pString.length()];
+ pString.getChars(0, chars.length, chars, 0);
+
+ for (int i = 0; i < chars.length / 2; i++) {
+ char temp = chars[i];
+ chars[i] = chars[chars.length - 1 - i];
+ chars[chars.length - 1 - i] = temp;
+ }
+
+ return new String(chars);
+ }
}
\ No newline at end of file
diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/lang/SystemUtil.java b/common/common-lang/src/main/java/com/twelvemonkeys/lang/SystemUtil.java
index 9e79347e..d824624b 100644
--- a/common/common-lang/src/main/java/com/twelvemonkeys/lang/SystemUtil.java
+++ b/common/common-lang/src/main/java/com/twelvemonkeys/lang/SystemUtil.java
@@ -1,688 +1,687 @@
-/*
- * 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 "TwelveMonkeys" 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 OWNER 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.lang;
-
-//import com.twelvemonkeys.util.XMLProperties;
-
-import java.io.*;
-import java.lang.reflect.Array;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * A utility class with some useful system-related functions.
- *
- * NOTE: This class is not considered part of the public API and may be
- * changed without notice
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/SystemUtil.java#3 $
- *
- */
-public final class SystemUtil {
- /** {@code ".xml"} */
- public static String XML_PROPERTIES = ".xml";
- /** {@code ".properties"} */
- public static String STD_PROPERTIES = ".properties";
-
- // Disallow creating objects of this type
- private SystemUtil() {
- }
-
- /** This class marks an inputstream as containing XML, does nothing */
- private static class XMLPropertiesInputStream extends FilterInputStream {
- public XMLPropertiesInputStream(InputStream pIS) {
- super(pIS);
- }
- }
-
- /**
- * Gets the named resource as a stream from the given Class' Classoader.
- * If the pGuessSuffix parameter is true, the method will try to append
- * typical properties file suffixes, such as ".properties" or ".xml".
- *
- * @param pClassLoader the class loader to use
- * @param pName name of the resource
- * @param pGuessSuffix guess suffix
- *
- * @return an input stream reading from the resource
- */
- private static InputStream getResourceAsStream(ClassLoader pClassLoader, String pName, boolean pGuessSuffix) {
- InputStream is;
-
- if (!pGuessSuffix) {
- is = pClassLoader.getResourceAsStream(pName);
-
- // If XML, wrap stream
- if (is != null && pName.endsWith(XML_PROPERTIES)) {
- is = new XMLPropertiesInputStream(is);
- }
- }
- else {
- // Try normal properties
- is = pClassLoader.getResourceAsStream(pName + STD_PROPERTIES);
-
- // Try XML
- if (is == null) {
- is = pClassLoader.getResourceAsStream(pName + XML_PROPERTIES);
-
- // Wrap stream
- if (is != null) {
- is = new XMLPropertiesInputStream(is);
- }
- }
- }
-
- // Return stream
- return is;
- }
-
- /**
- * Gets the named file as a stream from the current directory.
- * If the pGuessSuffix parameter is true, the method will try to append
- * typical properties file suffixes, such as ".properties" or ".xml".
- *
- * @param pName name of the resource
- * @param pGuessSuffix guess suffix
- *
- * @return an input stream reading from the resource
- */
- private static InputStream getFileAsStream(String pName, boolean pGuessSuffix) {
- InputStream is = null;
- File propertiesFile;
-
- try {
- if (!pGuessSuffix) {
- // Get file
- propertiesFile = new File(pName);
-
- if (propertiesFile.exists()) {
- is = new FileInputStream(propertiesFile);
-
- // If XML, wrap stream
- if (pName.endsWith(XML_PROPERTIES)) {
- is = new XMLPropertiesInputStream(is);
- }
- }
- }
- else {
- // Try normal properties
- propertiesFile = new File(pName + STD_PROPERTIES);
-
- if (propertiesFile.exists()) {
- is = new FileInputStream(propertiesFile);
- }
- else {
- // Try XML
- propertiesFile = new File(pName + XML_PROPERTIES);
-
- if (propertiesFile.exists()) {
- // Wrap stream
- is = new XMLPropertiesInputStream(new FileInputStream(propertiesFile));
- }
- }
- }
- }
- catch (FileNotFoundException fnf) {
- // Should not happen, as we always test that the file .exists()
- // before creating InputStream
- // assert false;
- }
-
- return is;
- }
-
- /**
- * Utility method for loading a named properties-file for a class.
- *
- * The properties-file is loaded through either:
- *
- * - The given class' class loader (from classpath)
- * - Or, the system class loader (from classpath)
- * - Or, if it cannot be found in the classpath, an attempt to read from
- * the current directory (or full path if given).
- *
- *
- * Both normal java.util.Properties and com.twelvemonkeys.util.XMLProperties
- * are supported (XML-properties must have ".xml" as its file extension).
- *
- * @param pClass The class to load properties for. If this parameter is
- * {@code null}, the method will work exactly as
- * {@link #loadProperties(String)}
- * @param pName The name of the properties-file. If this parameter is
- * {@code null}, the method will work exactly as
- * {@link #loadProperties(Class)}
- *
- * @return A Properties mapping read from the given file or for the given
- * class.
- *
- * @throws NullPointerException if both {@code pName} and
- * {@code pClass} paramters are {@code null}
- * @throws IOException if an error occurs during load.
- * @throws FileNotFoundException if no properties-file could be found.
- *
- * @see #loadProperties(String)
- * @see #loadProperties(Class)
- * @see java.lang.ClassLoader#getResourceAsStream
- * @see java.lang.ClassLoader#getSystemResourceAsStream
- *
- * @todo Reconsider ever using the System ClassLoader: http://www.javaworld.com/javaworld/javaqa/2003-06/01-qa-0606-load.html
- * @todo Consider using Context Classloader instead?
- */
- public static Properties loadProperties(Class pClass, String pName) throws IOException
- {
- // Convert to name the classloader understands
- String name = !StringUtil.isEmpty(pName) ? pName : pClass.getName().replace('.', '/');
-
- // Should we try to guess suffix?
- boolean guessSuffix = (pName == null || pName.indexOf('.') < 0);
-
- InputStream is;
-
- // TODO: WHAT IF MULTIPLE RESOURCES EXISTS?!
- // Try loading resource through the current class' classloader
- if (pClass != null && (is = getResourceAsStream(pClass.getClassLoader(), name, guessSuffix)) != null) {
- //&& (is = getResourceAsStream(pClass, name, guessSuffix)) != null) {
- // Nothing to do
- //System.out.println(((is instanceof XMLPropertiesInputStream) ?
- // "XML-properties" : "Normal .properties")
- // + " from Class' ClassLoader");
- }
- // If that fails, try the system classloader
- else if ((is = getResourceAsStream(ClassLoader.getSystemClassLoader(), name, guessSuffix)) != null) {
- //else if ((is = getSystemResourceAsStream(name, guessSuffix)) != null) {
- // Nothing to do
- //System.out.println(((is instanceof XMLPropertiesInputStream) ?
- // "XML-properties" : "Normal .properties")
- // + " from System ClassLoader");
- }
- // All failed, try loading from file
- else if ((is = getFileAsStream(name, guessSuffix)) != null) {
- //System.out.println(((is instanceof XMLPropertiesInputStream) ?
- // "XML-properties" : "Normal .properties")
- // + " from System ClassLoader");
- }
- else {
- if (guessSuffix) {
- // TODO: file extension iterator or something...
- throw new FileNotFoundException(name + ".properties or " + name + ".xml");
- }
- else {
- throw new FileNotFoundException(name);
- }
- }
-
- // We have inputstream now, load...
- try {
- return loadProperties(is);
- }
- finally {
- // NOTE: If is == null, a FileNotFoundException must have been thrown above
- try {
- is.close();
- }
- catch (IOException ioe) {
- // Not critical...
- }
- }
- }
-
- /**
- * Utility method for loading a properties-file for a given class.
- * The properties are searched for on the form
- * "com/package/ClassName.properties" or
- * "com/package/ClassName.xml".
- *
- * The properties-file is loaded through either:
- *
- * - The given class' class loader (from classpath)
- * - Or, the system class loader (from classpath)
- * - Or, if it cannot be found in the classpath, an attempt to read from
- * the current directory (or full path if given).
- *
- *
- * Both normal java.util.Properties and com.twelvemonkeys.util.XMLProperties
- * are supported (XML-properties must have ".xml" as its file extension).
- *
- * @param pClass The class to load properties for
- * @return A Properties mapping for the given class.
- *
- * @throws NullPointerException if the {@code pClass} paramters is
- * {@code null}
- * @throws IOException if an error occurs during load.
- * @throws FileNotFoundException if no properties-file could be found.
- *
- * @see #loadProperties(String)
- * @see #loadProperties(Class, String)
- * @see java.lang.ClassLoader#getResourceAsStream
- * @see java.lang.ClassLoader#getSystemResourceAsStream
- *
- */
- public static Properties loadProperties(Class pClass) throws IOException {
- return loadProperties(pClass, null);
- }
-
- /**
- * Utility method for loading a named properties-file.
- *
- * The properties-file is loaded through either:
- *
- * - The system class loader (from classpath)
- * - Or, if it cannot be found in the classpath, an attempt to read from
- * the current directory.
- *
- *
- * Both normal java.util.Properties and com.twelvemonkeys.util.XMLProperties
- * are supported (XML-properties must have ".xml" as its file extension).
- *
- * @param pName The name of the properties-file.
- * @return A Properties mapping read from the given file.
- *
- * @throws NullPointerException if the {@code pName} paramters is
- * {@code null}
- * @throws IOException if an error occurs during load.
- * @throws FileNotFoundException if no properties-file could be found.
- *
- * @see #loadProperties(Class)
- * @see #loadProperties(Class, String)
- * @see java.lang.ClassLoader#getSystemResourceAsStream
- *
- */
- public static Properties loadProperties(String pName) throws IOException {
- return loadProperties(null, pName);
- }
-
- /*
- * Utility method for loading a properties-file.
- *
- * The properties files may also be contained in a zip/jar-file named
- * by the {@code com.twelvemonkeys.util.Config} system property (use "java -D"
- * to override). Default is "config.zip" in the current directory.
- *
- * @param pName The name of the file to loaded
- * @return A Properties mapping for the given class. If no properties-
- * file was found, an empty Properties object is returned.
- *
- */
- /*
- public static Properties loadProperties(String pName) throws IOException {
- // Use XML?
- boolean useXML = pName.endsWith(XML_PROPERTIES) ? true : false;
-
- InputStream is = null;
-
- File file = new File(pName);
-
- String configName = System.getProperty("com.twelvemonkeys.util.Config");
- File configArchive = new File(!StringUtil.isEmpty(configName)
- ? configName : DEFAULT_CONFIG);
-
- // Get input stream to the file containing the properties
- if (file.exists()) {
- // Try reading from file, normal way
- is = new FileInputStream(file);
- }
- else if (configArchive.exists()) {
- // Try reading properties from zip-file
- ZipFile zip = new ZipFile(configArchive);
- ZipEntry ze = zip.getEntry(pName);
- if (ze != null) {
- is = zip.getInputStream(ze);
- }
-
- }
-
- // Do the loading
- try {
- // Load the properties
- return loadProperties(is, useXML);
- }
- finally {
- // Try closing the archive to free resources
- if (is != null) {
- try {
- is.close();
- }
- catch (IOException ioe) {
- // Not critical...
- }
- }
- }
-
- }
- */
-
- /**
- * Returns a Properties, loaded from the given inputstream. If the given
- * inputstream is null, then an empty Properties object is returned.
- *
- * @param pInput the inputstream to read from
- *
- * @return a Properties object read from the given stream, or an empty
- * Properties mapping, if the stream is null.
- *
- * @throws IOException if an error occurred when reading from the input
- * stream.
- *
- */
- private static Properties loadProperties(InputStream pInput)
- throws IOException {
-
- if (pInput == null) {
- throw new IllegalArgumentException("InputStream == null!");
- }
-
- Properties mapping = new Properties();
- /*if (pInput instanceof XMLPropertiesInputStream) {
- mapping = new XMLProperties();
- }
- else {
- mapping = new Properties();
- }*/
-
- // Load the properties
- mapping.load(pInput);
-
- return mapping;
- }
-
- @SuppressWarnings({"SuspiciousSystemArraycopy"})
- public static Object clone(final Cloneable pObject) throws CloneNotSupportedException {
- if (pObject == null) {
- return null; // Null is clonable.. Easy. ;-)
- }
-
- // All arrays does have a clone method, but it's invisible for reflection...
- // By luck, multi-dimensional primitive arrays are instances of Object[]
- if (pObject instanceof Object[]) {
- return ((Object[]) pObject).clone();
- }
- else if (pObject.getClass().isArray()) {
- // One-dimensional primitive array, cloned manually
- int lenght = Array.getLength(pObject);
- Object clone = Array.newInstance(pObject.getClass().getComponentType(), lenght);
- System.arraycopy(pObject, 0, clone, 0, lenght);
- return clone;
- }
-
- try {
- // Find the clone method
- Method clone = null;
- Class clazz = pObject.getClass();
- do {
- try {
- clone = clazz.getDeclaredMethod("clone");
- break; // Found, or throws exception above
- }
- catch (NoSuchMethodException ignore) {
- // Ignore
- }
- }
- while ((clazz = clazz.getSuperclass()) != null);
-
- // NOTE: This should never happen
- if (clone == null) {
- throw new CloneNotSupportedException(pObject.getClass().getName());
- }
-
- // Override access if needed
- if (!clone.isAccessible()) {
- clone.setAccessible(true);
- }
-
- // Invoke clone method on original object
- return clone.invoke(pObject);
- }
- catch (SecurityException e) {
- CloneNotSupportedException cns = new CloneNotSupportedException(pObject.getClass().getName());
- cns.initCause(e);
- throw cns;
- }
- catch (IllegalAccessException e) {
- throw new CloneNotSupportedException(pObject.getClass().getName());
- }
- catch (InvocationTargetException e) {
- if (e.getTargetException() instanceof CloneNotSupportedException) {
- throw (CloneNotSupportedException) e.getTargetException();
- }
- else if (e.getTargetException() instanceof RuntimeException) {
- throw (RuntimeException) e.getTargetException();
- }
- else if (e.getTargetException() instanceof Error) {
- throw (Error) e.getTargetException();
- }
-
- throw new CloneNotSupportedException(pObject.getClass().getName());
- }
- }
-
-// public static void loadLibrary(String pLibrary) {
-// NativeLoader.loadLibrary(pLibrary);
-// }
-//
-// public static void loadLibrary(String pLibrary, ClassLoader pLoader) {
-// NativeLoader.loadLibrary(pLibrary, pLoader);
-// }
-
- public static void main(String[] args) throws CloneNotSupportedException {
-
- System.out.println("clone: " + args.clone().length + " (" + args.length + ")");
- System.out.println("copy: " + ((String[]) clone(args)).length + " (" + args.length + ")");
-
- int[] ints = {1,2,3};
- int[] copies = (int[]) clone(ints);
- System.out.println("Copies: " + copies.length + " (" + ints.length + ")");
-
- int[][] intsToo = {{1}, {2,3}, {4,5,6}};
- int[][] copiesToo = (int[][]) clone(intsToo);
- System.out.println("Copies: " + copiesToo.length + " (" + intsToo.length + ")");
- System.out.println("Copies0: " + copiesToo[0].length + " (" + intsToo[0].length + ")");
- System.out.println("Copies1: " + copiesToo[1].length + " (" + intsToo[1].length + ")");
- System.out.println("Copies2: " + copiesToo[2].length + " (" + intsToo[2].length + ")");
-
- Map map = new HashMap();
-
- for (String arg : args) {
- map.put(arg, arg);
- }
-
- Map copy = (Map) clone((Cloneable) map);
-
- System.out.println("Map : " + map);
- System.out.println("Copy: " + copy);
-
- /*
- SecurityManager sm = System.getSecurityManager();
-
- try {
- System.setSecurityManager(new SecurityManager() {
- public void checkPermission(Permission perm) {
- if (perm.getName().equals("suppressAccessChecks")) {
- throw new SecurityException();
- }
- //super.checkPermission(perm);
- }
- });
- */
-
- Cloneable cloneable = new Cloneable() {}; // No public clone method
- Cloneable clone = (Cloneable) clone(cloneable);
-
- System.out.println("cloneable: " + cloneable);
- System.out.println("clone: " + clone);
-
- /*
- }
- finally {
- System.setSecurityManager(sm);
- }
- */
-
- AccessController.doPrivileged(new PrivilegedAction() {
- public Void run() {
- return null;
- }
- }, AccessController.getContext());
-
- //String string = args.length > 0 ? args[0] : "jaffa";
- //clone(string);
- }
-
- /**
- * Tests if a named class is generally available.
- * If a class is considered available, a call to
- * {@code Class.forName(pClassName)} will not result in an exception.
- *
- * @param pClassName the class name to test
- * @return {@code true} if available
- */
- public static boolean isClassAvailable(String pClassName) {
- return isClassAvailable(pClassName, (ClassLoader) null);
- }
-
- /**
- * Tests if a named class is available from another class.
- * If a class is considered available, a call to
- * {@code Class.forName(pClassName, true, pFromClass.getClassLoader())}
- * will not result in an exception.
- *
- * @param pClassName the class name to test
- * @param pFromClass the class to test from
- * @return {@code true} if available
- */
- public static boolean isClassAvailable(String pClassName, Class pFromClass) {
- ClassLoader loader = pFromClass != null ? pFromClass.getClassLoader() : null;
- return isClassAvailable(pClassName, loader);
- }
-
- private static boolean isClassAvailable(String pClassName, ClassLoader pLoader) {
- try {
- // TODO: Sometimes init is not needed, but need to find a way to know...
- getClass(pClassName, true, pLoader);
- return true;
- }
- catch (SecurityException ignore) {
- // Ignore
- }
- catch (ClassNotFoundException ignore) {
- // Ignore
- }
- catch (LinkageError ignore) {
- // Ignore
- }
-
- return false;
- }
-
- public static boolean isFieldAvailable(final String pClassName, final String pFieldName) {
- return isFieldAvailable(pClassName, pFieldName, (ClassLoader) null);
- }
-
- public static boolean isFieldAvailable(final String pClassName, final String pFieldName, final Class pFromClass) {
- ClassLoader loader = pFromClass != null ? pFromClass.getClassLoader() : null;
- return isFieldAvailable(pClassName, pFieldName, loader);
- }
-
- private static boolean isFieldAvailable(final String pClassName, final String pFieldName, final ClassLoader pLoader) {
- try {
- Class cl = getClass(pClassName, false, pLoader);
-
- Field field = cl.getField(pFieldName);
- if (field != null) {
- return true;
- }
- }
- catch (ClassNotFoundException ignore) {
- // Ignore
- }
- catch (LinkageError ignore) {
- // Ignore
- }
- catch (NoSuchFieldException ignore) {
- // Ignore
- }
- return false;
- }
-
- public static boolean isMethodAvailable(String pClassName, String pMethodName) {
- // Finds void only
- return isMethodAvailable(pClassName, pMethodName, null, (ClassLoader) null);
- }
-
- public static boolean isMethodAvailable(String pClassName, String pMethodName, Class[] pParams) {
- return isMethodAvailable(pClassName, pMethodName, pParams, (ClassLoader) null);
- }
-
- public static boolean isMethodAvailable(String pClassName, String pMethodName, Class[] pParams, Class pFromClass) {
- ClassLoader loader = pFromClass != null ? pFromClass.getClassLoader() : null;
- return isMethodAvailable(pClassName, pMethodName, pParams, loader);
- }
-
- private static boolean isMethodAvailable(String pClassName, String pMethodName, Class[] pParams, ClassLoader pLoader) {
- try {
- Class cl = getClass(pClassName, false, pLoader);
-
- Method method = cl.getMethod(pMethodName, pParams);
- if (method != null) {
- return true;
- }
- }
- catch (ClassNotFoundException ignore) {
- // Ignore
- }
- catch (LinkageError ignore) {
- // Ignore
- }
- catch (NoSuchMethodException ignore) {
- // Ignore
- }
- return false;
- }
-
- private static Class getClass(String pClassName, boolean pInitialize, ClassLoader pLoader) throws ClassNotFoundException {
- // NOTE: We need the context class loader, as SystemUtil's
- // class loader may have a totally different class loader than
- // the original caller class (as in Class.forName(cn, false, null)).
- ClassLoader loader = pLoader != null ? pLoader :
- Thread.currentThread().getContextClassLoader();
-
- return Class.forName(pClassName, pInitialize, loader);
- }
-}
+/*
+ * 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.lang;
+
+import java.io.*;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * A utility class with some useful system-related functions.
+ *
+ * NOTE: This class is not considered part of the public API and may be
+ * changed without notice
+ *
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/SystemUtil.java#3 $
+ *
+ */
+public final class SystemUtil {
+ /** {@code ".xml"} */
+ public static String XML_PROPERTIES = ".xml";
+ /** {@code ".properties"} */
+ public static String STD_PROPERTIES = ".properties";
+
+ // Disallow creating objects of this type
+ private SystemUtil() {
+ }
+
+ /** This class marks an inputstream as containing XML, does nothing */
+ private static class XMLPropertiesInputStream extends FilterInputStream {
+ public XMLPropertiesInputStream(InputStream pIS) {
+ super(pIS);
+ }
+ }
+
+ /**
+ * Gets the named resource as a stream from the given Class' Classoader.
+ * If the pGuessSuffix parameter is true, the method will try to append
+ * typical properties file suffixes, such as ".properties" or ".xml".
+ *
+ * @param pClassLoader the class loader to use
+ * @param pName name of the resource
+ * @param pGuessSuffix guess suffix
+ *
+ * @return an input stream reading from the resource
+ */
+ private static InputStream getResourceAsStream(ClassLoader pClassLoader, String pName, boolean pGuessSuffix) {
+ InputStream is;
+
+ if (!pGuessSuffix) {
+ is = pClassLoader.getResourceAsStream(pName);
+
+ // If XML, wrap stream
+ if (is != null && pName.endsWith(XML_PROPERTIES)) {
+ is = new XMLPropertiesInputStream(is);
+ }
+ }
+ else {
+ // Try normal properties
+ is = pClassLoader.getResourceAsStream(pName + STD_PROPERTIES);
+
+ // Try XML
+ if (is == null) {
+ is = pClassLoader.getResourceAsStream(pName + XML_PROPERTIES);
+
+ // Wrap stream
+ if (is != null) {
+ is = new XMLPropertiesInputStream(is);
+ }
+ }
+ }
+
+ // Return stream
+ return is;
+ }
+
+ /**
+ * Gets the named file as a stream from the current directory.
+ * If the pGuessSuffix parameter is true, the method will try to append
+ * typical properties file suffixes, such as ".properties" or ".xml".
+ *
+ * @param pName name of the resource
+ * @param pGuessSuffix guess suffix
+ *
+ * @return an input stream reading from the resource
+ */
+ private static InputStream getFileAsStream(String pName, boolean pGuessSuffix) {
+ InputStream is = null;
+ File propertiesFile;
+
+ try {
+ if (!pGuessSuffix) {
+ // Get file
+ propertiesFile = new File(pName);
+
+ if (propertiesFile.exists()) {
+ is = new FileInputStream(propertiesFile);
+
+ // If XML, wrap stream
+ if (pName.endsWith(XML_PROPERTIES)) {
+ is = new XMLPropertiesInputStream(is);
+ }
+ }
+ }
+ else {
+ // Try normal properties
+ propertiesFile = new File(pName + STD_PROPERTIES);
+
+ if (propertiesFile.exists()) {
+ is = new FileInputStream(propertiesFile);
+ }
+ else {
+ // Try XML
+ propertiesFile = new File(pName + XML_PROPERTIES);
+
+ if (propertiesFile.exists()) {
+ // Wrap stream
+ is = new XMLPropertiesInputStream(new FileInputStream(propertiesFile));
+ }
+ }
+ }
+ }
+ catch (FileNotFoundException fnf) {
+ // Should not happen, as we always test that the file .exists()
+ // before creating InputStream
+ // assert false;
+ }
+
+ return is;
+ }
+
+ /**
+ * Utility method for loading a named properties-file for a class.
+ *
+ * The properties-file is loaded through either:
+ *
+ * - The given class' class loader (from classpath)
+ * - Or, the system class loader (from classpath)
+ * - Or, if it cannot be found in the classpath, an attempt to read from
+ * the current directory (or full path if given).
+ *
+ *
+ * Both normal java.util.Properties and com.twelvemonkeys.util.XMLProperties
+ * are supported (XML-properties must have ".xml" as its file extension).
+ *
+ * @param pClass The class to load properties for. If this parameter is
+ * {@code null}, the method will work exactly as
+ * {@link #loadProperties(String)}
+ * @param pName The name of the properties-file. If this parameter is
+ * {@code null}, the method will work exactly as
+ * {@link #loadProperties(Class)}
+ *
+ * @return A Properties mapping read from the given file or for the given
+ * class.
+ *
+ * @throws NullPointerException if both {@code pName} and
+ * {@code pClass} paramters are {@code null}
+ * @throws IOException if an error occurs during load.
+ * @throws FileNotFoundException if no properties-file could be found.
+ *
+ * @see #loadProperties(String)
+ * @see #loadProperties(Class)
+ * @see java.lang.ClassLoader#getResourceAsStream
+ * @see java.lang.ClassLoader#getSystemResourceAsStream
+ */
+ // TODO: Reconsider ever using the System ClassLoader: http://www.javaworld.com/javaworld/javaqa/2003-06/01-qa-0606-load.html
+ // TODO: Consider using Context Classloader instead?
+ public static Properties loadProperties(Class pClass, String pName) throws IOException {
+ // Convert to name the classloader understands
+ String name = !StringUtil.isEmpty(pName) ? pName : pClass.getName().replace('.', '/');
+
+ // Should we try to guess suffix?
+ boolean guessSuffix = (pName == null || pName.indexOf('.') < 0);
+
+ InputStream is;
+
+ // TODO: WHAT IF MULTIPLE RESOURCES EXISTS?!
+ // Try loading resource through the current class' classloader
+ if (pClass != null && (is = getResourceAsStream(pClass.getClassLoader(), name, guessSuffix)) != null) {
+ //&& (is = getResourceAsStream(pClass, name, guessSuffix)) != null) {
+ // Nothing to do
+ //System.out.println(((is instanceof XMLPropertiesInputStream) ?
+ // "XML-properties" : "Normal .properties")
+ // + " from Class' ClassLoader");
+ }
+ // If that fails, try the system classloader
+ else if ((is = getResourceAsStream(ClassLoader.getSystemClassLoader(), name, guessSuffix)) != null) {
+ //else if ((is = getSystemResourceAsStream(name, guessSuffix)) != null) {
+ // Nothing to do
+ //System.out.println(((is instanceof XMLPropertiesInputStream) ?
+ // "XML-properties" : "Normal .properties")
+ // + " from System ClassLoader");
+ }
+ // All failed, try loading from file
+ else if ((is = getFileAsStream(name, guessSuffix)) != null) {
+ //System.out.println(((is instanceof XMLPropertiesInputStream) ?
+ // "XML-properties" : "Normal .properties")
+ // + " from System ClassLoader");
+ }
+ else {
+ if (guessSuffix) {
+ // TODO: file extension iterator or something...
+ throw new FileNotFoundException(name + ".properties or " + name + ".xml");
+ }
+ else {
+ throw new FileNotFoundException(name);
+ }
+ }
+
+ // We have inputstream now, load...
+ try {
+ return loadProperties(is);
+ }
+ finally {
+ // NOTE: If is == null, a FileNotFoundException must have been thrown above
+ try {
+ is.close();
+ }
+ catch (IOException ioe) {
+ // Not critical...
+ }
+ }
+ }
+
+ /**
+ * Utility method for loading a properties-file for a given class.
+ * The properties are searched for on the form
+ * "com/package/ClassName.properties" or
+ * "com/package/ClassName.xml".
+ *
+ * The properties-file is loaded through either:
+ *
+ * - The given class' class loader (from classpath)
+ * - Or, the system class loader (from classpath)
+ * - Or, if it cannot be found in the classpath, an attempt to read from
+ * the current directory (or full path if given).
+ *
+ *
+ * Both normal java.util.Properties and com.twelvemonkeys.util.XMLProperties
+ * are supported (XML-properties must have ".xml" as its file extension).
+ *
+ * @param pClass The class to load properties for
+ * @return A Properties mapping for the given class.
+ *
+ * @throws NullPointerException if the {@code pClass} paramters is
+ * {@code null}
+ * @throws IOException if an error occurs during load.
+ * @throws FileNotFoundException if no properties-file could be found.
+ *
+ * @see #loadProperties(String)
+ * @see #loadProperties(Class, String)
+ * @see java.lang.ClassLoader#getResourceAsStream
+ * @see java.lang.ClassLoader#getSystemResourceAsStream
+ *
+ */
+ public static Properties loadProperties(Class pClass) throws IOException {
+ return loadProperties(pClass, null);
+ }
+
+ /**
+ * Utility method for loading a named properties-file.
+ *
+ * The properties-file is loaded through either:
+ *
+ * - The system class loader (from classpath)
+ * - Or, if it cannot be found in the classpath, an attempt to read from
+ * the current directory.
+ *
+ *
+ * Both normal java.util.Properties and com.twelvemonkeys.util.XMLProperties
+ * are supported (XML-properties must have ".xml" as its file extension).
+ *
+ * @param pName The name of the properties-file.
+ * @return A Properties mapping read from the given file.
+ *
+ * @throws NullPointerException if the {@code pName} paramters is
+ * {@code null}
+ * @throws IOException if an error occurs during load.
+ * @throws FileNotFoundException if no properties-file could be found.
+ *
+ * @see #loadProperties(Class)
+ * @see #loadProperties(Class, String)
+ * @see java.lang.ClassLoader#getSystemResourceAsStream
+ *
+ */
+ public static Properties loadProperties(String pName) throws IOException {
+ return loadProperties(null, pName);
+ }
+
+ /*
+ * Utility method for loading a properties-file.
+ *
+ * The properties files may also be contained in a zip/jar-file named
+ * by the {@code com.twelvemonkeys.util.Config} system property (use "java -D"
+ * to override). Default is "config.zip" in the current directory.
+ *
+ * @param pName The name of the file to loaded
+ * @return A Properties mapping for the given class. If no properties-
+ * file was found, an empty Properties object is returned.
+ *
+ */
+ /*
+ public static Properties loadProperties(String pName) throws IOException {
+ // Use XML?
+ boolean useXML = pName.endsWith(XML_PROPERTIES) ? true : false;
+
+ InputStream is = null;
+
+ File file = new File(pName);
+
+ String configName = System.getProperty("com.twelvemonkeys.util.Config");
+ File configArchive = new File(!StringUtil.isEmpty(configName)
+ ? configName : DEFAULT_CONFIG);
+
+ // Get input stream to the file containing the properties
+ if (file.exists()) {
+ // Try reading from file, normal way
+ is = new FileInputStream(file);
+ }
+ else if (configArchive.exists()) {
+ // Try reading properties from zip-file
+ ZipFile zip = new ZipFile(configArchive);
+ ZipEntry ze = zip.getEntry(pName);
+ if (ze != null) {
+ is = zip.getInputStream(ze);
+ }
+
+ }
+
+ // Do the loading
+ try {
+ // Load the properties
+ return loadProperties(is, useXML);
+ }
+ finally {
+ // Try closing the archive to free resources
+ if (is != null) {
+ try {
+ is.close();
+ }
+ catch (IOException ioe) {
+ // Not critical...
+ }
+ }
+ }
+
+ }
+ */
+
+ /**
+ * Returns a Properties, loaded from the given inputstream. If the given
+ * inputstream is null, then an empty Properties object is returned.
+ *
+ * @param pInput the inputstream to read from
+ *
+ * @return a Properties object read from the given stream, or an empty
+ * Properties mapping, if the stream is null.
+ *
+ * @throws IOException if an error occurred when reading from the input
+ * stream.
+ *
+ */
+ private static Properties loadProperties(InputStream pInput)
+ throws IOException {
+
+ if (pInput == null) {
+ throw new IllegalArgumentException("InputStream == null!");
+ }
+
+ Properties mapping = new Properties();
+ /*if (pInput instanceof XMLPropertiesInputStream) {
+ mapping = new XMLProperties();
+ }
+ else {
+ mapping = new Properties();
+ }*/
+
+ // Load the properties
+ mapping.load(pInput);
+
+ return mapping;
+ }
+
+ @SuppressWarnings({"SuspiciousSystemArraycopy"})
+ public static Object clone(final Cloneable pObject) throws CloneNotSupportedException {
+ if (pObject == null) {
+ return null; // Null is clonable.. Easy. ;-)
+ }
+
+ // All arrays does have a clone method, but it's invisible for reflection...
+ // By luck, multi-dimensional primitive arrays are instances of Object[]
+ if (pObject instanceof Object[]) {
+ return ((Object[]) pObject).clone();
+ }
+ else if (pObject.getClass().isArray()) {
+ // One-dimensional primitive array, cloned manually
+ int lenght = Array.getLength(pObject);
+ Object clone = Array.newInstance(pObject.getClass().getComponentType(), lenght);
+ System.arraycopy(pObject, 0, clone, 0, lenght);
+ return clone;
+ }
+
+ try {
+ // Find the clone method
+ Method clone = null;
+ Class clazz = pObject.getClass();
+ do {
+ try {
+ clone = clazz.getDeclaredMethod("clone");
+ break; // Found, or throws exception above
+ }
+ catch (NoSuchMethodException ignore) {
+ // Ignore
+ }
+ }
+ while ((clazz = clazz.getSuperclass()) != null);
+
+ // NOTE: This should never happen
+ if (clone == null) {
+ throw new CloneNotSupportedException(pObject.getClass().getName());
+ }
+
+ // Override access if needed
+ if (!clone.isAccessible()) {
+ clone.setAccessible(true);
+ }
+
+ // Invoke clone method on original object
+ return clone.invoke(pObject);
+ }
+ catch (SecurityException e) {
+ CloneNotSupportedException cns = new CloneNotSupportedException(pObject.getClass().getName());
+ cns.initCause(e);
+ throw cns;
+ }
+ catch (IllegalAccessException e) {
+ throw new CloneNotSupportedException(pObject.getClass().getName());
+ }
+ catch (InvocationTargetException e) {
+ if (e.getTargetException() instanceof CloneNotSupportedException) {
+ throw (CloneNotSupportedException) e.getTargetException();
+ }
+ else if (e.getTargetException() instanceof RuntimeException) {
+ throw (RuntimeException) e.getTargetException();
+ }
+ else if (e.getTargetException() instanceof Error) {
+ throw (Error) e.getTargetException();
+ }
+
+ throw new CloneNotSupportedException(pObject.getClass().getName());
+ }
+ }
+
+// public static void loadLibrary(String pLibrary) {
+// NativeLoader.loadLibrary(pLibrary);
+// }
+//
+// public static void loadLibrary(String pLibrary, ClassLoader pLoader) {
+// NativeLoader.loadLibrary(pLibrary, pLoader);
+// }
+
+ public static void main(String[] args) throws CloneNotSupportedException {
+
+ System.out.println("clone: " + args.clone().length + " (" + args.length + ")");
+ System.out.println("copy: " + ((String[]) clone(args)).length + " (" + args.length + ")");
+
+ int[] ints = {1,2,3};
+ int[] copies = (int[]) clone(ints);
+ System.out.println("Copies: " + copies.length + " (" + ints.length + ")");
+
+ int[][] intsToo = {{1}, {2,3}, {4,5,6}};
+ int[][] copiesToo = (int[][]) clone(intsToo);
+ System.out.println("Copies: " + copiesToo.length + " (" + intsToo.length + ")");
+ System.out.println("Copies0: " + copiesToo[0].length + " (" + intsToo[0].length + ")");
+ System.out.println("Copies1: " + copiesToo[1].length + " (" + intsToo[1].length + ")");
+ System.out.println("Copies2: " + copiesToo[2].length + " (" + intsToo[2].length + ")");
+
+ Map map = new HashMap();
+
+ for (String arg : args) {
+ map.put(arg, arg);
+ }
+
+ Map copy = (Map) clone((Cloneable) map);
+
+ System.out.println("Map : " + map);
+ System.out.println("Copy: " + copy);
+
+ /*
+ SecurityManager sm = System.getSecurityManager();
+
+ try {
+ System.setSecurityManager(new SecurityManager() {
+ public void checkPermission(Permission perm) {
+ if (perm.getName().equals("suppressAccessChecks")) {
+ throw new SecurityException();
+ }
+ //super.checkPermission(perm);
+ }
+ });
+ */
+
+ Cloneable cloneable = new Cloneable() {}; // No public clone method
+ Cloneable clone = (Cloneable) clone(cloneable);
+
+ System.out.println("cloneable: " + cloneable);
+ System.out.println("clone: " + clone);
+
+ /*
+ }
+ finally {
+ System.setSecurityManager(sm);
+ }
+ */
+
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Void run() {
+ return null;
+ }
+ }, AccessController.getContext());
+
+ //String string = args.length > 0 ? args[0] : "jaffa";
+ //clone(string);
+ }
+
+ /**
+ * Tests if a named class is generally available.
+ * If a class is considered available, a call to
+ * {@code Class.forName(pClassName)} will not result in an exception.
+ *
+ * @param pClassName the class name to test
+ * @return {@code true} if available
+ */
+ public static boolean isClassAvailable(String pClassName) {
+ return isClassAvailable(pClassName, (ClassLoader) null);
+ }
+
+ /**
+ * Tests if a named class is available from another class.
+ * If a class is considered available, a call to
+ * {@code Class.forName(pClassName, true, pFromClass.getClassLoader())}
+ * will not result in an exception.
+ *
+ * @param pClassName the class name to test
+ * @param pFromClass the class to test from
+ * @return {@code true} if available
+ */
+ public static boolean isClassAvailable(String pClassName, Class pFromClass) {
+ ClassLoader loader = pFromClass != null ? pFromClass.getClassLoader() : null;
+ return isClassAvailable(pClassName, loader);
+ }
+
+ private static boolean isClassAvailable(String pClassName, ClassLoader pLoader) {
+ try {
+ // TODO: Sometimes init is not needed, but need to find a way to know...
+ getClass(pClassName, true, pLoader);
+ return true;
+ }
+ catch (SecurityException ignore) {
+ // Ignore
+ }
+ catch (ClassNotFoundException ignore) {
+ // Ignore
+ }
+ catch (LinkageError ignore) {
+ // Ignore
+ }
+
+ return false;
+ }
+
+ public static boolean isFieldAvailable(final String pClassName, final String pFieldName) {
+ return isFieldAvailable(pClassName, pFieldName, (ClassLoader) null);
+ }
+
+ public static boolean isFieldAvailable(final String pClassName, final String pFieldName, final Class pFromClass) {
+ ClassLoader loader = pFromClass != null ? pFromClass.getClassLoader() : null;
+ return isFieldAvailable(pClassName, pFieldName, loader);
+ }
+
+ private static boolean isFieldAvailable(final String pClassName, final String pFieldName, final ClassLoader pLoader) {
+ try {
+ Class cl = getClass(pClassName, false, pLoader);
+
+ Field field = cl.getField(pFieldName);
+ if (field != null) {
+ return true;
+ }
+ }
+ catch (ClassNotFoundException ignore) {
+ // Ignore
+ }
+ catch (LinkageError ignore) {
+ // Ignore
+ }
+ catch (NoSuchFieldException ignore) {
+ // Ignore
+ }
+ return false;
+ }
+
+ public static boolean isMethodAvailable(String pClassName, String pMethodName) {
+ // Finds void only
+ return isMethodAvailable(pClassName, pMethodName, null, (ClassLoader) null);
+ }
+
+ public static boolean isMethodAvailable(String pClassName, String pMethodName, Class[] pParams) {
+ return isMethodAvailable(pClassName, pMethodName, pParams, (ClassLoader) null);
+ }
+
+ public static boolean isMethodAvailable(String pClassName, String pMethodName, Class[] pParams, Class pFromClass) {
+ ClassLoader loader = pFromClass != null ? pFromClass.getClassLoader() : null;
+ return isMethodAvailable(pClassName, pMethodName, pParams, loader);
+ }
+
+ private static boolean isMethodAvailable(String pClassName, String pMethodName, Class[] pParams, ClassLoader pLoader) {
+ try {
+ Class cl = getClass(pClassName, false, pLoader);
+
+ Method method = cl.getMethod(pMethodName, pParams);
+ if (method != null) {
+ return true;
+ }
+ }
+ catch (ClassNotFoundException ignore) {
+ // Ignore
+ }
+ catch (LinkageError ignore) {
+ // Ignore
+ }
+ catch (NoSuchMethodException ignore) {
+ // Ignore
+ }
+ return false;
+ }
+
+ private static Class getClass(String pClassName, boolean pInitialize, ClassLoader pLoader) throws ClassNotFoundException {
+ // NOTE: We need the context class loader, as SystemUtil's
+ // class loader may have a totally different class loader than
+ // the original caller class (as in Class.forName(cn, false, null)).
+ ClassLoader loader = pLoader != null ? pLoader :
+ Thread.currentThread().getContextClassLoader();
+
+ return Class.forName(pClassName, pInitialize, loader);
+ }
+}
diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/lang/Validate.java b/common/common-lang/src/main/java/com/twelvemonkeys/lang/Validate.java
index 16897f31..6c61981d 100755
--- a/common/common-lang/src/main/java/com/twelvemonkeys/lang/Validate.java
+++ b/common/common-lang/src/main/java/com/twelvemonkeys/lang/Validate.java
@@ -1,3 +1,33 @@
+/*
+ * Copyright (c) 2009, 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.lang;
import java.util.Arrays;
@@ -6,10 +36,11 @@ import java.util.Map;
/**
* Kind of like {@code org.apache.commons.lang.Validate}. Just smarter. ;-)
- *
+ *
* Uses type parameterized return values, thus making it possible to check
* constructor arguments before
* they are passed on to {@code super} or {@code this} type constructors.
+ *
*
* @author Harald Kuhr
* @author last modified by $Author: haku $
diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/lang/package_info.java b/common/common-lang/src/main/java/com/twelvemonkeys/lang/package_info.java
index ced3971b..92c3c925 100755
--- a/common/common-lang/src/main/java/com/twelvemonkeys/lang/package_info.java
+++ b/common/common-lang/src/main/java/com/twelvemonkeys/lang/package_info.java
@@ -1,3 +1,33 @@
+/*
+ * 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.
+ */
+
/**
*Contains utils/helpers for classes in {@code java.lang}.
*/
diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/util/AbstractDecoratedMap.java b/common/common-lang/src/main/java/com/twelvemonkeys/util/AbstractDecoratedMap.java
index ab4e4fb7..bb144842 100755
--- a/common/common-lang/src/main/java/com/twelvemonkeys/util/AbstractDecoratedMap.java
+++ b/common/common-lang/src/main/java/com/twelvemonkeys/util/AbstractDecoratedMap.java
@@ -1,400 +1,404 @@
-/*
- * 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 "TwelveMonkeys" 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 OWNER 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.util;
-
-import java.util.*;
-import java.io.Serializable;
-
-/**
- * AbstractDecoratedMap
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/AbstractDecoratedMap.java#2 $
- */
-// TODO: The generics in this class looks suspicious..
-abstract class AbstractDecoratedMap extends AbstractMap implements Map, Serializable, Cloneable {
- protected Map> entries;
- protected transient volatile int modCount;
-
- private transient volatile Set> entrySet = null;
- private transient volatile Set keySet = null;
- private transient volatile Collection values = null;
-
- /**
- * Creates a {@code Map} backed by a {@code HashMap}.
- */
- public AbstractDecoratedMap() {
- this(new HashMap>(), null);
- }
-
- /**
- * Creates a {@code Map} backed by a {@code HashMap}, containing all
- * key/value mappings from the given {@code Map}.
- *
- * This is constructor is here to comply with the reccomendations for
- * "standard" constructors in the {@code Map} interface.
- *
- * @see #AbstractDecoratedMap(java.util.Map, java.util.Map)
- *
- * @param pContents the map whose mappings are to be placed in this map.
- * May be {@code null}.
- */
- public AbstractDecoratedMap(Map extends K, ? extends V> pContents) {
- this(new HashMap>(), pContents);
- }
-
- /**
- * Creates a {@code Map} backed by the given backing-{@code Map},
- * containing all key/value mappings from the given contents-{@code Map}.
- *
- * NOTE: The backing map is structuraly cahnged, and it should NOT be
- * accessed directly, after the wrapped map is created.
- *
- * @param pBacking the backing map of this map. Must be either empty, or
- * the same map as {@code pContents}.
- * @param pContents the map whose mappings are to be placed in this map.
- * May be {@code null}.
- *
- * @throws IllegalArgumentException if {@code pBacking} is {@code null}
- * or if {@code pBacking} differs from {@code pContent} and is not empty.
- */
- public AbstractDecoratedMap(Map> pBacking, Map extends K, ? extends V> pContents) {
- if (pBacking == null) {
- throw new IllegalArgumentException("backing == null");
- }
-
- Entry extends K, ? extends V>[] entries = null;
- if (pBacking == pContents) {
- // NOTE: Special treatment to avoid ClassCastExceptions
- Set extends Entry extends K, ? extends V>> es = pContents.entrySet();
- //noinspection unchecked
- entries = new Entry[es.size()];
- entries = es.toArray(entries);
- pContents = null;
- pBacking.clear();
- }
- else if (!pBacking.isEmpty()) {
- throw new IllegalArgumentException("backing must be empty");
- }
-
- this.entries = pBacking;
- init();
-
- if (pContents != null) {
- putAll(pContents);
- }
- else if (entries != null) {
- // Reinsert entries, this time wrapped
- for (Entry extends K, ? extends V> entry : entries) {
- put(entry.getKey(), entry.getValue());
- }
- }
- }
-
- /**
- * Default implementation, does nothing.
- */
- protected void init() {
- }
-
- public int size() {
- return entries.size();
- }
-
- public void clear() {
- entries.clear();
- modCount++;
- init();
- }
-
- public boolean isEmpty() {
- return entries.isEmpty();
- }
-
- public boolean containsKey(Object pKey) {
- return entries.containsKey(pKey);
- }
-
- /**
- * Returns {@code true} if this map maps one or more keys to the
- * specified pValue. More formally, returns {@code true} if and only if
- * this map contains at least one mapping to a pValue {@code v} such that
- * {@code (pValue==null ? v==null : pValue.equals(v))}.
- *
- * This implementation requires time linear in the map size for this
- * operation.
- *
- * @param pValue pValue whose presence in this map is to be tested.
- * @return {@code true} if this map maps one or more keys to the
- * specified pValue.
- */
- public boolean containsValue(Object pValue) {
- for (V value : values()) {
- if (value == pValue || (value != null && value.equals(pValue))) {
- return true;
- }
- }
-
- return false;
- }
-
- public Collection values() {
- Collection values = this.values;
- return values != null ? values : (this.values = new Values());
- }
-
- public Set> entrySet() {
- Set> es = entrySet;
- return es != null ? es : (entrySet = new EntrySet());
- }
-
- public Set keySet() {
- Set ks = keySet;
- return ks != null ? ks : (keySet = new KeySet());
- }
-
- /**
- * Returns a shallow copy of this {@code AbstractMap} instance: the keys
- * and values themselves are not cloned.
- *
- * @return a shallow copy of this map.
- */
- protected Object clone() throws CloneNotSupportedException {
- AbstractDecoratedMap map = (AbstractDecoratedMap) super.clone();
-
- map.values = null;
- map.entrySet = null;
- map.keySet = null;
-
- // TODO: Implement: Need to clone the backing map...
-
- return map;
- }
-
- // Subclass overrides these to alter behavior of views' iterator() method
- protected abstract Iterator newKeyIterator();
-
- protected abstract Iterator newValueIterator();
-
- protected abstract Iterator> newEntryIterator();
-
- // TODO: Implement these (get/put/remove)?
- public abstract V get(Object pKey);
-
- public abstract V remove(Object pKey);
-
- public abstract V put(K pKey, V pValue);
-
- /*protected*/ Entry createEntry(K pKey, V pValue) {
- return new BasicEntry(pKey, pValue);
- }
-
- /*protected*/ Entry getEntry(K pKey) {
- return entries.get(pKey);
- }
-
- /**
- * Removes the given entry from the Map.
- *
- * @param pEntry the entry to be removed
- *
- * @return the removed entry, or {@code null} if nothing was removed.
- */
- protected Entry removeEntry(Entry pEntry) {
- if (pEntry == null) {
- return null;
- }
-
- // Find candidate entry for this key
- Entry candidate = getEntry(pEntry.getKey());
- if (candidate == pEntry || (candidate != null && candidate.equals(pEntry))) {
- // Remove
- remove(pEntry.getKey());
- return pEntry;
- }
- return null;
- }
-
- protected class Values extends AbstractCollection {
- public Iterator iterator() {
- return newValueIterator();
- }
-
- public int size() {
- return AbstractDecoratedMap.this.size();
- }
-
- public boolean contains(Object o) {
- return containsValue(o);
- }
-
- public void clear() {
- AbstractDecoratedMap.this.clear();
- }
- }
-
- protected class EntrySet extends AbstractSet> {
- public Iterator> iterator() {
- return newEntryIterator();
- }
-
- public boolean contains(Object o) {
- if (!(o instanceof Entry))
- return false;
- Entry e = (Entry) o;
-
- //noinspection SuspiciousMethodCalls
- Entry candidate = entries.get(e.getKey());
- return candidate != null && candidate.equals(e);
- }
-
- public boolean remove(Object o) {
- if (!(o instanceof Entry)) {
- return false;
- }
-
- /*
- // NOTE: Extra cautions is taken, to only remove the entry if it
- // equals the entry in the map
- Object key = ((Entry) o).getKey();
- Entry entry = (Entry) entries.get(key);
-
- // Same entry?
- if (entry != null && entry.equals(o)) {
- return AbstractWrappedMap.this.remove(key) != null;
- }
-
- return false;
- */
-
- //noinspection unchecked
- return AbstractDecoratedMap.this.removeEntry((Entry) o) != null;
- }
-
- public int size() {
- return AbstractDecoratedMap.this.size();
- }
-
- public void clear() {
- AbstractDecoratedMap.this.clear();
- }
- }
-
- protected class KeySet extends AbstractSet {
- public Iterator iterator() {
- return newKeyIterator();
- }
- public int size() {
- return AbstractDecoratedMap.this.size();
- }
- public boolean contains(Object o) {
- return containsKey(o);
- }
- public boolean remove(Object o) {
- return AbstractDecoratedMap.this.remove(o) != null;
- }
- public void clear() {
- AbstractDecoratedMap.this.clear();
- }
- }
-
- /**
- * A simple Map.Entry implementaton.
- */
- static class BasicEntry implements Entry, Serializable {
- K mKey;
- V mValue;
-
- BasicEntry(K pKey, V pValue) {
- mKey = pKey;
- mValue = pValue;
- }
-
- /**
- * Default implementation does nothing.
- *
- * @param pMap the map that is accessed
- */
- protected void recordAccess(Map pMap) {
- }
-
- /**
- * Default implementation does nothing.
- * @param pMap the map that is removed from
- */
- protected void recordRemoval(Map pMap) {
- }
-
- public V getValue() {
- return mValue;
- }
-
- public V setValue(V pValue) {
- V oldValue = mValue;
- mValue = pValue;
- return oldValue;
- }
-
- public K getKey() {
- return mKey;
- }
-
- public boolean equals(Object pOther) {
- if (!(pOther instanceof Map.Entry)) {
- return false;
- }
-
- Map.Entry entry = (Map.Entry) pOther;
-
- Object k1 = mKey;
- Object k2 = entry.getKey();
-
- if (k1 == k2 || (k1 != null && k1.equals(k2))) {
- Object v1 = mValue;
- Object v2 = entry.getValue();
-
- if (v1 == v2 || (v1 != null && v1.equals(v2))) {
- return true;
- }
- }
-
- return false;
- }
-
- public int hashCode() {
- return (mKey == null ? 0 : mKey.hashCode()) ^
- (mValue == null ? 0 : mValue.hashCode());
- }
-
- public String toString() {
- return getKey() + "=" + getValue();
- }
- }
+/*
+ * 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.util;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * AbstractDecoratedMap
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/AbstractDecoratedMap.java#2 $
+ */
+// TODO: The generics in this class looks suspicious..
+abstract class AbstractDecoratedMap extends AbstractMap implements Map, Serializable, Cloneable {
+ protected Map> entries;
+ protected transient volatile int modCount;
+
+ private transient volatile Set> entrySet = null;
+ private transient volatile Set keySet = null;
+ private transient volatile Collection values = null;
+
+ /**
+ * Creates a {@code Map} backed by a {@code HashMap}.
+ */
+ public AbstractDecoratedMap() {
+ this(new HashMap>(), null);
+ }
+
+ /**
+ * Creates a {@code Map} backed by a {@code HashMap}, containing all
+ * key/value mappings from the given {@code Map}.
+ *
+ * This is constructor is here to comply with the reccomendations for
+ * "standard" constructors in the {@code Map} interface.
+ *
+ *
+ * @see #AbstractDecoratedMap(java.util.Map, java.util.Map)
+ *
+ * @param pContents the map whose mappings are to be placed in this map.
+ * May be {@code null}.
+ */
+ public AbstractDecoratedMap(Map extends K, ? extends V> pContents) {
+ this(new HashMap>(), pContents);
+ }
+
+ /**
+ * Creates a {@code Map} backed by the given backing-{@code Map},
+ * containing all key/value mappings from the given contents-{@code Map}.
+ *
+ * NOTE: The backing map is structuraly cahnged, and it should NOT be
+ * accessed directly, after the wrapped map is created.
+ *
+ *
+ * @param pBacking the backing map of this map. Must be either empty, or
+ * the same map as {@code pContents}.
+ * @param pContents the map whose mappings are to be placed in this map.
+ * May be {@code null}.
+ *
+ * @throws IllegalArgumentException if {@code pBacking} is {@code null}
+ * or if {@code pBacking} differs from {@code pContent} and is not empty.
+ */
+ public AbstractDecoratedMap(Map> pBacking, Map extends K, ? extends V> pContents) {
+ if (pBacking == null) {
+ throw new IllegalArgumentException("backing == null");
+ }
+
+ Entry extends K, ? extends V>[] entries = null;
+ if (pBacking == pContents) {
+ // NOTE: Special treatment to avoid ClassCastExceptions
+ Set extends Entry extends K, ? extends V>> es = pContents.entrySet();
+ //noinspection unchecked
+ entries = new Entry[es.size()];
+ entries = es.toArray(entries);
+ pContents = null;
+ pBacking.clear();
+ }
+ else if (!pBacking.isEmpty()) {
+ throw new IllegalArgumentException("backing must be empty");
+ }
+
+ this.entries = pBacking;
+ init();
+
+ if (pContents != null) {
+ putAll(pContents);
+ }
+ else if (entries != null) {
+ // Reinsert entries, this time wrapped
+ for (Entry extends K, ? extends V> entry : entries) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ /**
+ * Default implementation, does nothing.
+ */
+ protected void init() {
+ }
+
+ public int size() {
+ return entries.size();
+ }
+
+ public void clear() {
+ entries.clear();
+ modCount++;
+ init();
+ }
+
+ public boolean isEmpty() {
+ return entries.isEmpty();
+ }
+
+ public boolean containsKey(Object pKey) {
+ return entries.containsKey(pKey);
+ }
+
+ /**
+ * Returns {@code true} if this map maps one or more keys to the
+ * specified pValue. More formally, returns {@code true} if and only if
+ * this map contains at least one mapping to a pValue {@code v} such that
+ * {@code (pValue==null ? v==null : pValue.equals(v))}.
+ *
+ * This implementation requires time linear in the map size for this
+ * operation.
+ *
+ *
+ * @param pValue pValue whose presence in this map is to be tested.
+ * @return {@code true} if this map maps one or more keys to the
+ * specified pValue.
+ */
+ public boolean containsValue(Object pValue) {
+ for (V value : values()) {
+ if (value == pValue || (value != null && value.equals(pValue))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public Collection values() {
+ Collection values = this.values;
+ return values != null ? values : (this.values = new Values());
+ }
+
+ public Set> entrySet() {
+ Set> es = entrySet;
+ return es != null ? es : (entrySet = new EntrySet());
+ }
+
+ public Set keySet() {
+ Set ks = keySet;
+ return ks != null ? ks : (keySet = new KeySet());
+ }
+
+ /**
+ * Returns a shallow copy of this {@code AbstractMap} instance: the keys
+ * and values themselves are not cloned.
+ *
+ * @return a shallow copy of this map.
+ */
+ protected Object clone() throws CloneNotSupportedException {
+ AbstractDecoratedMap map = (AbstractDecoratedMap) super.clone();
+
+ map.values = null;
+ map.entrySet = null;
+ map.keySet = null;
+
+ // TODO: Implement: Need to clone the backing map...
+
+ return map;
+ }
+
+ // Subclass overrides these to alter behavior of views' iterator() method
+ protected abstract Iterator newKeyIterator();
+
+ protected abstract Iterator newValueIterator();
+
+ protected abstract Iterator> newEntryIterator();
+
+ // TODO: Implement these (get/put/remove)?
+ public abstract V get(Object pKey);
+
+ public abstract V remove(Object pKey);
+
+ public abstract V put(K pKey, V pValue);
+
+ /*protected*/ Entry createEntry(K pKey, V pValue) {
+ return new BasicEntry(pKey, pValue);
+ }
+
+ /*protected*/ Entry getEntry(K pKey) {
+ return entries.get(pKey);
+ }
+
+ /**
+ * Removes the given entry from the Map.
+ *
+ * @param pEntry the entry to be removed
+ *
+ * @return the removed entry, or {@code null} if nothing was removed.
+ */
+ protected Entry removeEntry(Entry pEntry) {
+ if (pEntry == null) {
+ return null;
+ }
+
+ // Find candidate entry for this key
+ Entry candidate = getEntry(pEntry.getKey());
+ if (candidate == pEntry || (candidate != null && candidate.equals(pEntry))) {
+ // Remove
+ remove(pEntry.getKey());
+ return pEntry;
+ }
+ return null;
+ }
+
+ protected class Values extends AbstractCollection {
+ public Iterator iterator() {
+ return newValueIterator();
+ }
+
+ public int size() {
+ return AbstractDecoratedMap.this.size();
+ }
+
+ public boolean contains(Object o) {
+ return containsValue(o);
+ }
+
+ public void clear() {
+ AbstractDecoratedMap.this.clear();
+ }
+ }
+
+ protected class EntrySet extends AbstractSet> {
+ public Iterator> iterator() {
+ return newEntryIterator();
+ }
+
+ public boolean contains(Object o) {
+ if (!(o instanceof Entry))
+ return false;
+ Entry e = (Entry) o;
+
+ //noinspection SuspiciousMethodCalls
+ Entry candidate = entries.get(e.getKey());
+ return candidate != null && candidate.equals(e);
+ }
+
+ public boolean remove(Object o) {
+ if (!(o instanceof Entry)) {
+ return false;
+ }
+
+ /*
+ // NOTE: Extra cautions is taken, to only remove the entry if it
+ // equals the entry in the map
+ Object key = ((Entry) o).getKey();
+ Entry entry = (Entry) entries.get(key);
+
+ // Same entry?
+ if (entry != null && entry.equals(o)) {
+ return AbstractWrappedMap.this.remove(key) != null;
+ }
+
+ return false;
+ */
+
+ //noinspection unchecked
+ return AbstractDecoratedMap.this.removeEntry((Entry) o) != null;
+ }
+
+ public int size() {
+ return AbstractDecoratedMap.this.size();
+ }
+
+ public void clear() {
+ AbstractDecoratedMap.this.clear();
+ }
+ }
+
+ protected class KeySet extends AbstractSet {
+ public Iterator iterator() {
+ return newKeyIterator();
+ }
+ public int size() {
+ return AbstractDecoratedMap.this.size();
+ }
+ public boolean contains(Object o) {
+ return containsKey(o);
+ }
+ public boolean remove(Object o) {
+ return AbstractDecoratedMap.this.remove(o) != null;
+ }
+ public void clear() {
+ AbstractDecoratedMap.this.clear();
+ }
+ }
+
+ /**
+ * A simple Map.Entry implementation.
+ */
+ static class BasicEntry implements Entry, Serializable {
+ K mKey;
+ V mValue;
+
+ BasicEntry(K pKey, V pValue) {
+ mKey = pKey;
+ mValue = pValue;
+ }
+
+ /**
+ * Default implementation does nothing.
+ *
+ * @param pMap the map that is accessed
+ */
+ protected void recordAccess(Map pMap) {
+ }
+
+ /**
+ * Default implementation does nothing.
+ * @param pMap the map that is removed from
+ */
+ protected void recordRemoval(Map pMap) {
+ }
+
+ public V getValue() {
+ return mValue;
+ }
+
+ public V setValue(V pValue) {
+ V oldValue = mValue;
+ mValue = pValue;
+ return oldValue;
+ }
+
+ public K getKey() {
+ return mKey;
+ }
+
+ public boolean equals(Object pOther) {
+ if (!(pOther instanceof Map.Entry)) {
+ return false;
+ }
+
+ Map.Entry entry = (Map.Entry) pOther;
+
+ Object k1 = mKey;
+ Object k2 = entry.getKey();
+
+ if (k1 == k2 || (k1 != null && k1.equals(k2))) {
+ Object v1 = mValue;
+ Object v2 = entry.getValue();
+
+ if (v1 == v2 || (v1 != null && v1.equals(v2))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public int hashCode() {
+ return (mKey == null ? 0 : mKey.hashCode()) ^
+ (mValue == null ? 0 : mValue.hashCode());
+ }
+
+ public String toString() {
+ return getKey() + "=" + getValue();
+ }
+ }
}
\ No newline at end of file
diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/util/AbstractTokenIterator.java b/common/common-lang/src/main/java/com/twelvemonkeys/util/AbstractTokenIterator.java
index 90e56161..bd4e284c 100755
--- a/common/common-lang/src/main/java/com/twelvemonkeys/util/AbstractTokenIterator.java
+++ b/common/common-lang/src/main/java/com/twelvemonkeys/util/AbstractTokenIterator.java
@@ -1,87 +1,88 @@
-/*
- * 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 "TwelveMonkeys" 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 OWNER 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.util;
-
-/**
- * Abstract base class for {@code TokenIterator}s to extend.
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/AbstractTokenIterator.java#1 $
- */
-public abstract class AbstractTokenIterator implements TokenIterator {
-
- /**
- * Not supported.
- *
- * @throws UnsupportedOperationException {@code remove} is not supported by
- * this Iterator.
- */
- public void remove() {
- // TODO: This is not difficult:
- // - Convert String to StringBuilder in constructor
- // - delete(pos, next.lenght())
- // - Add toString() method
- // BUT: Would it ever be useful? :-)
-
- throw new UnsupportedOperationException("remove");
- }
-
- public final boolean hasMoreTokens() {
- return hasNext();
- }
-
- /**
- * Returns the next element in the iteration as a {@code String}.
- * This implementation simply returns {@code (String) next()}.
- *
- * @return the next element in the iteration.
- * @exception java.util.NoSuchElementException iteration has no more elements.
- * @see #next()
- */
- public final String nextToken() {
- return next();
- }
-
- /**
- * This implementation simply returns {@code hasNext()}.
- * @see #hasNext()
- */
- public final boolean hasMoreElements() {
- return hasNext();
- }
-
- /**
- * This implementation simply returns {@code next()}.
- * @see #next()
- */
- public final String nextElement() {
- return next();
- }
-}
+/*
+ * 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.util;
+
+/**
+ * Abstract base class for {@code TokenIterator}s to extend.
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/AbstractTokenIterator.java#1 $
+ */
+public abstract class AbstractTokenIterator implements TokenIterator {
+
+ /**
+ * Not supported.
+ *
+ * @throws UnsupportedOperationException {@code remove} is not supported by
+ * this Iterator.
+ */
+ public void remove() {
+ // TODO: This is not difficult:
+ // - Convert String to StringBuilder in constructor
+ // - delete(pos, next.lenght())
+ // - Add toString() method
+ // BUT: Would it ever be useful? :-)
+
+ throw new UnsupportedOperationException("remove");
+ }
+
+ public final boolean hasMoreTokens() {
+ return hasNext();
+ }
+
+ /**
+ * Returns the next element in the iteration as a {@code String}.
+ * This implementation simply returns {@code (String) next()}.
+ *
+ * @return the next element in the iteration.
+ * @exception java.util.NoSuchElementException iteration has no more elements.
+ * @see #next()
+ */
+ public final String nextToken() {
+ return next();
+ }
+
+ /**
+ * This implementation simply returns {@code hasNext()}.
+ * @see #hasNext()
+ */
+ public final boolean hasMoreElements() {
+ return hasNext();
+ }
+
+ /**
+ * This implementation simply returns {@code next()}.
+ * @see #next()
+ */
+ public final String nextElement() {
+ return next();
+ }
+}
diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/util/BeanMap.java b/common/common-lang/src/main/java/com/twelvemonkeys/util/BeanMap.java
index 8f830c00..6dd0edbe 100755
--- a/common/common-lang/src/main/java/com/twelvemonkeys/util/BeanMap.java
+++ b/common/common-lang/src/main/java/com/twelvemonkeys/util/BeanMap.java
@@ -1,245 +1,248 @@
-/*
- * 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 "TwelveMonkeys" 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 OWNER 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.util;
-
-import java.beans.IndexedPropertyDescriptor;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.*;
-import java.io.Serializable;
-
-/**
- * A {@code Map} adapter for a Java Bean.
- *
- * Ruthlessly stolen from
- * initDescriptors(Object pBean) throws IntrospectionException {
- final Set descriptors = new HashSet();
-
- PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(pBean.getClass()).getPropertyDescriptors();
- for (PropertyDescriptor descriptor : propertyDescriptors) {
- // Skip Object.getClass(), as you probably never want it
- if ("class".equals(descriptor.getName()) && descriptor.getPropertyType() == Class.class) {
- continue;
- }
-
- // Only support simple setter/getters.
- if (!(descriptor instanceof IndexedPropertyDescriptor)) {
- descriptors.add(descriptor);
- }
- }
-
- return Collections.unmodifiableSet(descriptors);
- }
-
- public Set> entrySet() {
- return new BeanSet();
- }
-
- public Object get(final Object pKey) {
- return super.get(pKey);
- }
-
- public Object put(final String pKey, final Object pValue) {
- checkKey(pKey);
-
- for (Entry entry : entrySet()) {
- if (entry.getKey().equals(pKey)) {
- return entry.setValue(pValue);
- }
- }
-
- return null;
- }
-
- public Object remove(final Object pKey) {
- return super.remove(checkKey(pKey));
- }
-
- public int size() {
- return descriptors.size();
- }
-
- private String checkKey(final Object pKey) {
- if (pKey == null) {
- throw new IllegalArgumentException("key == null");
- }
- // NB - the cast forces CCE if key is the wrong type.
- final String name = (String) pKey;
-
- if (!containsKey(name)) {
- throw new IllegalArgumentException("Bad key: " + pKey);
- }
-
- return name;
- }
-
- private Object readResolve() throws IntrospectionException {
- // Initialize the property descriptors
- descriptors = initDescriptors(bean);
- return this;
- }
-
- private class BeanSet extends AbstractSet> {
- public Iterator> iterator() {
- return new BeanIterator(descriptors.iterator());
- }
-
- public int size() {
- return descriptors.size();
- }
- }
-
- private class BeanIterator implements Iterator> {
- private final Iterator mIterator;
-
- public BeanIterator(final Iterator pIterator) {
- mIterator = pIterator;
- }
-
- public boolean hasNext() {
- return mIterator.hasNext();
- }
-
- public BeanEntry next() {
- return new BeanEntry(mIterator.next());
- }
-
- public void remove() {
- mIterator.remove();
- }
- }
-
- private class BeanEntry implements Entry {
- private final PropertyDescriptor mDescriptor;
-
- public BeanEntry(final PropertyDescriptor pDescriptor) {
- this.mDescriptor = pDescriptor;
- }
-
- public String getKey() {
- return mDescriptor.getName();
- }
-
- public Object getValue() {
- return unwrap(new Wrapped() {
- public Object run() throws IllegalAccessException, InvocationTargetException {
- final Method method = mDescriptor.getReadMethod();
- // A write-only bean.
- if (method == null) {
- throw new UnsupportedOperationException("No getter: " + mDescriptor.getName());
- }
-
- return method.invoke(bean);
- }
- });
- }
-
- public Object setValue(final Object pValue) {
- return unwrap(new Wrapped() {
- public Object run() throws IllegalAccessException, InvocationTargetException {
- final Method method = mDescriptor.getWriteMethod();
- // A read-only bean.
- if (method == null) {
- throw new UnsupportedOperationException("No write method for property: " + mDescriptor.getName());
- }
-
- final Object old = getValue();
- method.invoke(bean, pValue);
- return old;
- }
- });
- }
-
- public boolean equals(Object pOther) {
- if (!(pOther instanceof Map.Entry)) {
- return false;
- }
-
- Map.Entry entry = (Map.Entry) pOther;
-
- Object k1 = getKey();
- Object k2 = entry.getKey();
-
- if (k1 == k2 || (k1 != null && k1.equals(k2))) {
- Object v1 = getValue();
- Object v2 = entry.getValue();
-
- if (v1 == v2 || (v1 != null && v1.equals(v2))) {
- return true;
- }
- }
-
- return false;
- }
-
- public int hashCode() {
- return (getKey() == null ? 0 : getKey().hashCode()) ^
- (getValue() == null ? 0 : getValue().hashCode());
- }
-
- public String toString() {
- return getKey() + "=" + getValue();
- }
- }
-
- private static interface Wrapped {
- Object run() throws IllegalAccessException, InvocationTargetException;
- }
-
- private static Object unwrap(final Wrapped wrapped) {
- try {
- return wrapped.run();
- }
- catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- }
- catch (final InvocationTargetException e) {
- // Javadocs for setValue indicate cast is ok.
- throw (RuntimeException) e.getCause();
- }
- }
+/*
+ * 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.util;
+
+import java.beans.IndexedPropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ * A {@code Map} adapter for a Java Bean.
+ *
+ * Ruthlessly stolen from
+ * Binkley's Blog
+ *
+ */
+public final class BeanMap extends AbstractMap implements Serializable, Cloneable {
+ private final Object bean;
+ private transient Set descriptors;
+
+ public BeanMap(Object pBean) throws IntrospectionException {
+ if (pBean == null) {
+ throw new IllegalArgumentException("bean == null");
+ }
+
+ bean = pBean;
+ descriptors = initDescriptors(pBean);
+ }
+
+ private static Set initDescriptors(Object pBean) throws IntrospectionException {
+ final Set descriptors = new HashSet();
+
+ PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(pBean.getClass()).getPropertyDescriptors();
+ for (PropertyDescriptor descriptor : propertyDescriptors) {
+ // Skip Object.getClass(), as you probably never want it
+ if ("class".equals(descriptor.getName()) && descriptor.getPropertyType() == Class.class) {
+ continue;
+ }
+
+ // Only support simple setter/getters.
+ if (!(descriptor instanceof IndexedPropertyDescriptor)) {
+ descriptors.add(descriptor);
+ }
+ }
+
+ return Collections.unmodifiableSet(descriptors);
+ }
+
+ public Set> entrySet() {
+ return new BeanSet();
+ }
+
+ public Object get(final Object pKey) {
+ return super.get(pKey);
+ }
+
+ public Object put(final String pKey, final Object pValue) {
+ checkKey(pKey);
+
+ for (Entry entry : entrySet()) {
+ if (entry.getKey().equals(pKey)) {
+ return entry.setValue(pValue);
+ }
+ }
+
+ return null;
+ }
+
+ public Object remove(final Object pKey) {
+ return super.remove(checkKey(pKey));
+ }
+
+ public int size() {
+ return descriptors.size();
+ }
+
+ private String checkKey(final Object pKey) {
+ if (pKey == null) {
+ throw new IllegalArgumentException("key == null");
+ }
+ // NB - the cast forces CCE if key is the wrong type.
+ final String name = (String) pKey;
+
+ if (!containsKey(name)) {
+ throw new IllegalArgumentException("Bad key: " + pKey);
+ }
+
+ return name;
+ }
+
+ private Object readResolve() throws IntrospectionException {
+ // Initialize the property descriptors
+ descriptors = initDescriptors(bean);
+ return this;
+ }
+
+ private class BeanSet extends AbstractSet