OSDN Git Service

Add new source files
[armadillo/armadillo1.git] / src / jp / sfjp / armadillo / archive / lzh / LzhFile.java
1 package jp.sfjp.armadillo.archive.lzh;
2
3 import java.io.*;
4 import java.nio.channels.*;
5 import jp.sfjp.armadillo.archive.*;
6 import jp.sfjp.armadillo.io.*;
7
8 public final class LzhFile extends ArchiveFile {
9
10     private File afile;
11     private RandomAccessFile raf;
12     private LzhHeader header;
13     private LzhEntry ongoingEntry;
14     private final LzhChecksum cksum;
15     private final byte[] buffer;
16
17     public LzhFile(File afile) {
18         this.afile = afile;
19         this.header = new LzhHeader();
20         this.cksum = new LzhChecksum();
21         this.buffer = new byte[8192];
22     }
23
24     public LzhFile(File afile, boolean withOpen) throws IOException {
25         this(afile);
26         open();
27     }
28
29     @Override
30     public ArchiveEntry newEntry(String name) {
31         LzhEntry entry = new LzhEntry(LzhHeader.HEADER_LEVEL_2);
32         entry.setName(name);
33         if (name.endsWith("/"))
34             entry.method = LzhMethod.LHD;
35         return entry;
36     }
37
38     @Override
39     public void open() throws IOException {
40         if (raf != null)
41             throw new IOException("the file has been already opened");
42         if (!afile.exists())
43             afile.createNewFile();
44         this.raf = new RandomAccessFile(afile, "rw");
45         this.opened = true;
46     }
47
48     @Override
49     public void reset() throws IOException {
50         ongoingEntry = null;
51         currentPosition = 0L;
52         raf.seek(0L);
53     }
54
55     @Override
56     public ArchiveEntry nextEntry() throws IOException {
57         ensureOpen();
58         if (ongoingEntry == null)
59             currentPosition = 0L;
60         else
61             currentPosition += ongoingEntry.compressedSize;
62         raf.seek(currentPosition);
63         ongoingEntry = readCurrentEntry();
64         currentPosition = raf.getFilePointer();
65         return ongoingEntry;
66     }
67
68     @Override
69     public void addEntry(ArchiveEntry entry, InputStream is, long length) throws IOException {
70         final long p;
71         if (raf.length() == 0)
72             p = 0;
73         else
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);
78         if (length > 0) {
79             final long p1 = raf.getFilePointer();
80             writeData(newEntry, is, os, length);
81             raf.seek(p);
82             header.write(os, newEntry);
83             assert raf.getFilePointer() == p1;
84             raf.seek(p1 + newEntry.compressedSize);
85         }
86         raf.write(0);
87         raf.seek(p);
88         currentPosition = p;
89     }
90
91     private void writeData(LzhEntry entry, InputStream is, OutputStream os, long length) throws LzhException, IOException {
92         final long p = raf.getFilePointer();
93         cksum.reset();
94         // XXX better way to flush remaining bits
95         OutputStream los = LzhOutputStream.openStream(new AntiCloseOutputStream(os),
96                                                       entry.getMethod());
97         OutputStream ios = new InspectionOutputStream(los, cksum);
98         final long written = IOUtilities.transfer(buffer, is, ios, length);
99         assert written == length;
100         los.close();
101         entry.compressedSize = raf.getFilePointer() - p;
102         entry.crc = cksum.getShortValue();
103     }
104
105     @Override
106     public void updateEntry(ArchiveEntry entry, InputStream is, long length) throws IOException {
107         removeEntry(entry);
108         addEntry(entry, is, length);
109     }
110
111     @Override
112     public void removeEntry(ArchiveEntry entry) throws IOException {
113         if (!seek(entry))
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);
119     }
120
121     LzhEntry readCurrentEntry() throws IOException {
122         return header.read(Channels.newInputStream(raf.getChannel()));
123     }
124
125     void truncate(final long offset, final long length) throws IOException {
126         long p2 = offset;
127         long p1 = p2 + length;
128         while (true) {
129             raf.seek(p1);
130             final int r = raf.read(buffer);
131             if (r <= 0)
132                 break;
133             raf.seek(p2);
134             raf.write(buffer, 0, r);
135             p2 += r;
136             p1 += r;
137         }
138         raf.setLength(raf.length() - length);
139         reset();
140     }
141
142     @Override
143     public void close() throws IOException {
144         try {
145             raf.close();
146         }
147         finally {
148             super.close();
149             afile = null;
150             header = null;
151         }
152     }
153
154     private static final class AntiCloseOutputStream extends BufferedOutputStream {
155
156         public AntiCloseOutputStream(OutputStream os) {
157             super(os, 32768);
158         }
159
160         @Override
161         public void close() throws IOException {
162             // ignore
163         }
164
165     }
166
167 }