aQute.lib.putjar.DirectoryInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of biz.aQute.bndlib Show documentation
Show all versions of biz.aQute.bndlib Show documentation
bndlib: A Swiss Army Knife for OSGi
package aQute.lib.putjar;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.CRC32;
import aQute.lib.io.IOConstants;
import aQute.libg.fileiterator.FileIterator;
public class DirectoryInputStream extends InputStream {
static final int BUFFER_SIZE = IOConstants.PAGE_SIZE * 16;
final File root;
final FileIterator fi;
File element;
int entries = 0;
int state = START;
long where = 0;
final static int START = 0;
final static int HEADER = 1;
final static int DATA = 2;
final static int DIRECTORY = 4;
final static int EOF = 5;
final static InputStream eof = new ByteArrayInputStream(new byte[0]);
ByteArrayOutputStream directory = new ByteArrayOutputStream();
InputStream current = eof;
public DirectoryInputStream(File dir) {
root = dir;
fi = new FileIterator(dir);
}
@Override
public int read() throws IOException {
if (fi == null)
return -1;
int c = current.read();
if (c < 0) {
next();
c = current.read();
}
if (c >= 0)
where++;
return c;
}
void next() throws IOException {
switch (state) {
case START :
case DATA :
nextHeader();
break;
case HEADER :
if (element.isFile() && element.length() > 0) {
current = new FileInputStream(element);
state = DATA;
} else
nextHeader();
break;
case DIRECTORY :
state = EOF;
current = eof;
break;
case EOF :
break;
}
}
private void nextHeader() throws IOException {
if (fi.hasNext()) {
element = fi.next();
state = HEADER;
current = getHeader(root, element);
entries++;
} else {
current = getDirectory();
state = DIRECTORY;
}
}
/**
*
* end of central dir signature 4 bytes (0x06054b50) number of this
* disk 2 bytes number of the disk with the start of the central directory 2
* bytes total number of entries in the central directory on this disk 2
* bytes total number of entries in the central directory 2 bytes size of
* the central directory 4 bytes offset of start of central directory with
* respect to the starting disk number 4 bytes .ZIP file comment length 2
* bytes .ZIP file comment (variable size)
*
*/
InputStream getDirectory() throws IOException {
long where = this.where;
int sizeDirectory = directory.size();
writeInt(directory, 0x504b0506); // Signature
writeShort(directory, 0); // # of disk
writeShort(directory, 0); // # of the disk with start of the central
// dir
writeShort(directory, entries); // # of entries
writeInt(directory, sizeDirectory); // Size of central dir
writeInt(directory, (int) where);
writeShort(directory, 0);
directory.close();
byte[] data = directory.toByteArray();
return new ByteArrayInputStream(data);
}
private void writeShort(OutputStream out, int v) throws IOException {
for (int i = 0; i < 2; i++) {
out.write((byte) (v & 0xFF));
v = v >> 8;
}
}
private void writeInt(OutputStream out, int v) throws IOException {
for (int i = 0; i < 4; i++) {
out.write((byte) (v & 0xFF));
v = v >> 8;
}
}
/**
* Local file header:
*
*
* local file header signature 4 bytes (0x04034b50)
* version needed to extract 2 bytes general purpose bit flag 2 bytes
* compression method 2 bytes last mod file time 2 bytes last mod file date
* 2 bytes crc-32 4 bytes compressed size 4 bytes uncompressed size 4 bytes
* file name length 2 bytes extra field length 2 bytes file name (variable
* size) extra field (variable size) central file header signature 4 bytes
* (0x02014b50) version made by 2 bytes version needed to extract 2 bytes
* general purpose bit flag 2 bytes compression method 2 bytes last mod file
* time 2 bytes last mod file date 2 bytes crc-32 4 bytes compressed size 4
* bytes uncompressed size 4 bytes file name length 2 bytes extra field
* length 2 bytes file comment length 2 bytes disk number start 2 bytes
* internal file attributes 2 bytes external file attributes 4 bytes
* relative offset of local header 4 bytes file name (variable size) extra
* field (variable size) file comment (variable size)
*
*
*/
private InputStream getHeader(File root, File file) throws IOException {
long where = this.where;
ByteArrayOutputStream bout = new ByteArrayOutputStream();
// Signature
writeInt(bout, 0x04034b50);
writeInt(directory, 0x504b0102);
// Version needed to extract
writeShort(directory, 0);
// Version needed to extract
writeShort(bout, 10);
writeShort(directory, 10);
// General purpose bit flag (use descriptor)
writeShort(bout, 0); // descriptor follows data
writeShort(directory, 0); // descriptor follows data
// Compresson method (stored)
writeShort(bout, 0);
writeShort(directory, 0);
// Mod time
writeInt(bout, 0);
writeInt(directory, 0);
if (file.isDirectory()) {
writeInt(bout, 0); // CRC
writeInt(bout, 0); // Compressed size
writeInt(bout, 0); // Uncompressed Size
writeInt(directory, 0);
writeInt(directory, 0);
writeInt(directory, 0);
} else {
CRC32 crc = getCRC(file);
writeInt(bout, (int) crc.getValue());
writeInt(bout, (int) file.length());
writeInt(bout, (int) file.length());
writeInt(directory, (int) crc.getValue());
writeInt(directory, (int) file.length());
writeInt(directory, (int) file.length());
}
String p = getPath(root, file);
if (file.isDirectory())
p = p + "/";
byte[] path = p.getBytes("UTF-8");
writeShort(bout, path.length);
writeShort(directory, path.length);
writeShort(bout, 0); // extra length
writeShort(directory, 0);
bout.write(path);
writeShort(directory, 0); // File comment length
writeShort(directory, 0); // disk number start 2 bytes
writeShort(directory, 0); // internal file attributes 2 bytes
writeInt(directory, 0); // external file attributes 4 bytes
writeInt(directory, (int) where); // relative offset of local header 4
// bytes
directory.write(path);
byte[] bytes = bout.toByteArray();
return new ByteArrayInputStream(bytes);
}
private String getPath(File root, File file) {
if (file.equals(root))
return "";
String p = getPath(root, file.getParentFile());
if (p.length() == 0)
p = file.getName();
else {
p = p + "/" + file.getName();
}
return p;
}
private CRC32 getCRC(File file) throws IOException {
CRC32 crc = new CRC32();
FileInputStream in = new FileInputStream(file);
try {
byte data[] = new byte[BUFFER_SIZE];
int size = in.read(data);
while (size > 0) {
crc.update(data, 0, size);
size = in.read(data);
}
} finally {
in.close();
}
return crc;
}
}