1 package jp.sfjp.armadillo.archive.lzh;
4 import java.nio.channels.*;
5 import jp.sfjp.armadillo.archive.*;
6 import jp.sfjp.armadillo.io.*;
8 public final class LzhFile extends ArchiveFile {
11 private RandomAccessFile raf;
12 private LzhHeader header;
13 private LzhEntry ongoingEntry;
14 private final LzhChecksum cksum;
15 private final byte[] buffer;
17 public LzhFile(File afile) {
19 this.header = new LzhHeader();
20 this.cksum = new LzhChecksum();
21 this.buffer = new byte[8192];
24 public LzhFile(File afile, boolean withOpen) throws IOException {
30 public ArchiveEntry newEntry(String name) {
31 LzhEntry entry = new LzhEntry(LzhHeader.HEADER_LEVEL_2);
33 if (name.endsWith("/"))
34 entry.method = LzhMethod.LHD;
39 public void open() throws IOException {
41 throw new IOException("the file has been already opened");
43 afile.createNewFile();
44 this.raf = new RandomAccessFile(afile, "rw");
49 public void reset() throws IOException {
56 public ArchiveEntry nextEntry() throws IOException {
58 if (ongoingEntry == null)
61 currentPosition += ongoingEntry.compressedSize;
62 raf.seek(currentPosition);
63 ongoingEntry = readCurrentEntry();
64 currentPosition = raf.getFilePointer();
69 public void addEntry(ArchiveEntry entry, InputStream is, long length) throws IOException {
71 if (raf.length() == 0)
74 raf.seek(p = raf.length() - 1);
75 OutputStream os = Channels.newOutputStream(raf.getChannel());
76 LzhEntry newEntry = LzhArchiveCreator.toLzhEntry(entry);
77 header.write(os, newEntry);
79 final long p1 = raf.getFilePointer();
80 writeData(newEntry, is, os, length);
82 header.write(os, newEntry);
83 assert raf.getFilePointer() == p1;
84 raf.seek(p1 + newEntry.compressedSize);
91 private void writeData(LzhEntry entry, InputStream is, OutputStream os, long length) throws LzhException, IOException {
92 final long p = raf.getFilePointer();
94 // XXX better way to flush remaining bits
95 OutputStream los = LzhOutputStream.openStream(new AntiCloseOutputStream(os),
97 OutputStream ios = new InspectionOutputStream(los, cksum);
98 final long written = IOUtilities.transfer(buffer, is, ios, length);
99 assert written == length;
101 entry.compressedSize = raf.getFilePointer() - p;
102 entry.crc = cksum.getShortValue();
106 public void updateEntry(ArchiveEntry entry, InputStream is, long length) throws IOException {
108 addEntry(entry, is, length);
112 public void removeEntry(ArchiveEntry entry) throws IOException {
114 throw new LzhException("entry " + entry + " not found");
115 assert ongoingEntry != null;
116 final int headerLength = ongoingEntry.headerLength;
117 final long bodyLength = ongoingEntry.compressedSize;
118 truncate(currentPosition - headerLength, headerLength + bodyLength);
121 LzhEntry readCurrentEntry() throws IOException {
122 return header.read(Channels.newInputStream(raf.getChannel()));
125 void truncate(final long offset, final long length) throws IOException {
127 long p1 = p2 + length;
130 final int r = raf.read(buffer);
134 raf.write(buffer, 0, r);
138 raf.setLength(raf.length() - length);
143 public void close() throws IOException {
154 private static final class AntiCloseOutputStream extends BufferedOutputStream {
156 public AntiCloseOutputStream(OutputStream os) {
161 public void close() throws IOException {