package org.amse.ys.zip; import java.io.*; import java.util.*; import org.geometerplus.zlibrary.core.util.InputStreamHolder; public final class ZipFile { private final static Comparator<String> ourIgnoreCaseComparator = new Comparator<String>() { @Override public final int compare(String s0, String s1) { return s0.compareToIgnoreCase(s1); } }; private final InputStreamHolder myStreamHolder; private final Map<String,LocalFileHeader> myFileHeaders = new TreeMap<String,LocalFileHeader>(ourIgnoreCaseComparator); private boolean myAllFilesAreRead; public ZipFile(final String fileName) { this(new InputStreamHolder() { public InputStream getInputStream() throws IOException { return new FileInputStream(fileName); } }); } public ZipFile(final File file) { this(new InputStreamHolder() { public InputStream getInputStream() throws IOException { return new FileInputStream(file); } }); } public ZipFile(InputStreamHolder streamHolder) { myStreamHolder = streamHolder; } public Collection<LocalFileHeader> headers() { try { readAllHeaders(); } catch (IOException e) { } return myFileHeaders.values(); } private boolean readFileHeader(MyBufferedInputStream baseStream, String fileToFind) throws IOException { LocalFileHeader header = new LocalFileHeader(); header.readFrom(baseStream); if (header.Signature != LocalFileHeader.FILE_HEADER_SIGNATURE) { return false; } if (header.FileName != null) { myFileHeaders.put(header.FileName, header); if (header.FileName.equalsIgnoreCase(fileToFind)) { return true; } } if ((header.Flags & 0x08) == 0) { baseStream.skip(header.CompressedSize); } else { findAndReadDescriptor(baseStream, header); } return false; } private void readAllHeaders() throws IOException { if (myAllFilesAreRead) { return; } myAllFilesAreRead = true; MyBufferedInputStream baseStream = getBaseStream(); baseStream.setPosition(0); myFileHeaders.clear(); try { while (baseStream.available() > 0) { readFileHeader(baseStream, null); } } finally { storeBaseStream(baseStream); } } /** * Finds descriptor of the last header and installs sizes of files */ private void findAndReadDescriptor(MyBufferedInputStream baseStream, LocalFileHeader header) throws IOException { final Decompressor decompressor = Decompressor.init(baseStream, header); int uncompressedSize = 0; while (true) { int blockSize = decompressor.read(null, 0, 2048); if (blockSize <= 0) { break; } uncompressedSize += blockSize; } header.UncompressedSize = uncompressedSize; Decompressor.storeDecompressor(decompressor); } private final Queue<MyBufferedInputStream> myStoredStreams = new LinkedList<MyBufferedInputStream>(); synchronized void storeBaseStream(MyBufferedInputStream baseStream) { myStoredStreams.add(baseStream); } synchronized MyBufferedInputStream getBaseStream() throws IOException { final MyBufferedInputStream stored = myStoredStreams.poll(); if (stored != null) { return stored; } return new MyBufferedInputStream(myStreamHolder); } private ZipInputStream createZipInputStream(LocalFileHeader header) throws IOException { return new ZipInputStream(this, header); } public boolean entryExists(String entryName) { try { return getHeader(entryName) != null; } catch (IOException e) { return false; } } public int getEntrySize(String entryName) throws IOException { return getHeader(entryName).UncompressedSize; } public InputStream getInputStream(String entryName) throws IOException { return createZipInputStream(getHeader(entryName)); } public LocalFileHeader getHeader(String entryName) throws IOException { if (!myFileHeaders.isEmpty()) { LocalFileHeader header = myFileHeaders.get(entryName); if (header != null) { return header; } if (myAllFilesAreRead) { throw new ZipException("Entry " + entryName + " is not found"); } } // ready to read file header MyBufferedInputStream baseStream = getBaseStream(); baseStream.setPosition(0); try { while (baseStream.available() > 0 && !readFileHeader(baseStream, entryName)) { } final LocalFileHeader header = myFileHeaders.get(entryName); if (header != null) { return header; } } finally { storeBaseStream(baseStream); } throw new ZipException("Entry " + entryName + " is not found"); } }