/*
* Copyright 2010 NCHOVY
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.krakenapps.util.directoryfile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public class SplitDirectoryFileInputStream extends InputStream {
private final long unitSize;
private final File fileBase;
private DirectoryFileArchive dfa;
private InputStream stream;
private int currentId;
private int lastId;
public SplitDirectoryFileInputStream(long unitSize, DirectoryFileArchive dfa, File file) throws IOException {
this.unitSize = unitSize;
this.fileBase = file;
this.dfa = dfa;
this.dfa.attach();
init();
}
public SplitDirectoryFileInputStream(long unitSize, DirectoryFileArchive dfa, String name) throws IOException {
this.unitSize = unitSize;
this.fileBase = new File(name);
this.dfa = dfa;
this.dfa.attach();
init();
}
protected InputStream getInputStream(File file) throws IOException {
return dfa.getInputStreamAbsolutePath(getCurrentFile(fileBase, currentId).getAbsolutePath());
}
public DirectoryFileArchive getDirectoryFileArchive() {
return this.dfa;
}
private void init() throws IOException {
currentId = 0;
lastId = SplitDirectoryFileOutputStream.getLastId(dfa, fileBase);
if (lastId == -1)
throw new IllegalStateException("getLastId should not return -1.");
stream = getInputStream(getCurrentFile(fileBase, currentId));
}
private static File getCurrentFile(File base, int id) {
if (id > 0)
return new File(base.getPath() + "." + id);
else
return base;
}
@Override
public int read() throws IOException {
int ret = stream.read();
if (ret == -1) {
if (initNextStream())
return stream.read();
else
return -1;
}
return ret;
}
private boolean initNextStream() throws IOException {
currentId++;
if (currentId > lastId)
return false;
stream.close();
stream = getInputStream(getCurrentFile(fileBase, currentId));
return true;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (len > unitSize) {
return bulkRead(b, off, len);
}
int read = stream.read(b, off, len);
if (read == -1) {
if (initNextStream())
return stream.read(b, off, len);
else
return -1;
}
if (read == len) {
return read;
} else {
int remain = len - read;
if (initNextStream()) {
int anotherRead = stream.read(b, off + read, remain);
if (anotherRead == -1)
return read;
return read + anotherRead;
} else {
return read;
}
}
}
private int bulkRead(byte[] b, int off, int len) throws IOException {
// len > unitSize
int currOff = off;
int totalRead = 0;
while (len > 0) {
int read = stream.read(b, currOff, (int) unitSize);
if (read == -1) {
if (initNextStream()) {
continue;
} else {
return totalRead;
}
} else {
len -= read;
totalRead += read;
}
}
return totalRead;
}
@Override
public long skip(long n) throws IOException {
int available = stream.available();
int skip = 0;
if (n > available && initNextStream()) {
n -= available;
skip += available;
while (n > unitSize) {
initNextStream();
n -= unitSize;
skip += unitSize;
}
return stream.skip(n) + skip;
} else {
return stream.skip(n);
}
}
@Override
public int available() throws IOException {
int remainingFiles = lastId - currentId;
// @formatter:off
if (remainingFiles <= 0)
return stream.available();
if (remainingFiles == 1) {
return (int) (stream.available()
+ dfa.getActualSize(dfa.getSubPath(SplitDirectoryFileOutputStream.getLastFile(fileBase, lastId))));
} else {
return (int) (stream.available()
+ unitSize * (remainingFiles - 2)
+ dfa.getActualSize(dfa.getSubPath(SplitDirectoryFileOutputStream.getLastFile(fileBase, lastId))));
}
// @formatter:on
}
@Override
public void close() throws IOException {
stream.close();
dfa.close();
}
@Override
public boolean markSupported() {
return false;
}
}