source: trunk/gcc/libjava/java/util/zip/ZipOutputStream.java

Last change on this file was 1392, checked in by bird, 21 years ago

This commit was generated by cvs2svn to compensate for changes in r1391,
which included commits to RCS files with non-trunk default branches.

  • Property cvs2svn:cvs-rev set to 1.1.1.2
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 11.3 KB
Line 
1/* java.util.zip.ZipOutputStream
2 Copyright (C) 2001 Free Software Foundation, Inc.
3
4This file is part of GNU Classpath.
5
6GNU Classpath is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Classpath is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Classpath; see the file COPYING. If not, write to the
18Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
1902111-1307 USA.
20
21Linking this library statically or dynamically with other modules is
22making a combined work based on this library. Thus, the terms and
23conditions of the GNU General Public License cover the whole
24combination.
25
26As a special exception, the copyright holders of this library give you
27permission to link this library with independent modules to produce an
28executable, regardless of the license terms of these independent
29modules, and to copy and distribute the resulting executable under
30terms of your choice, provided that you also meet, for each linked
31independent module, the terms and conditions of the license of that
32module. An independent module is a module which is not derived from
33or based on this library. If you modify this library, you may extend
34this exception to your version of the library, but you are not
35obligated to do so. If you do not wish to do so, delete this
36exception statement from your version. */
37
38package java.util.zip;
39import java.io.OutputStream;
40import java.io.IOException;
41import java.io.UnsupportedEncodingException;
42import java.util.Vector;
43import java.util.Enumeration;
44
45/**
46 * This is a FilterOutputStream that writes the files into a zip
47 * archive one after another. It has a special method to start a new
48 * zip entry. The zip entries contains information about the file name
49 * size, compressed size, CRC, etc.
50 *
51 * It includes support for STORED and DEFLATED entries.
52 *
53 * This class is not thread safe.
54 *
55 * @author Jochen Hoenicke
56 */
57public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants
58{
59 private Vector entries = new Vector();
60 private CRC32 crc = new CRC32();
61 private ZipEntry curEntry = null;
62
63 private int curMethod;
64 private int size;
65 private int offset = 0;
66
67 private byte[] zipComment = new byte[0];
68 private int defaultMethod = DEFLATED;
69
70 /**
71 * Our Zip version is hard coded to 1.0 resp. 2.0
72 */
73 private final static int ZIP_STORED_VERSION = 10;
74 private final static int ZIP_DEFLATED_VERSION = 20;
75
76 /**
77 * Compression method. This method doesn't compress at all.
78 */
79 public final static int STORED = 0;
80 /**
81 * Compression method. This method uses the Deflater.
82 */
83 public final static int DEFLATED = 8;
84
85 /**
86 * Creates a new Zip output stream, writing a zip archive.
87 * @param out the output stream to which the zip archive is written.
88 */
89 public ZipOutputStream(OutputStream out)
90 {
91 super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
92 }
93
94 /**
95 * Set the zip file comment.
96 * @param comment the comment.
97 * @exception IllegalArgumentException if encoding of comment is
98 * longer than 0xffff bytes.
99 */
100 public void setComment(String comment)
101 {
102 byte[] commentBytes;
103 commentBytes = comment.getBytes();
104 if (commentBytes.length > 0xffff)
105 throw new IllegalArgumentException("Comment too long.");
106 zipComment = commentBytes;
107 }
108
109 /**
110 * Sets default compression method. If the Zip entry specifies
111 * another method its method takes precedence.
112 * @param method the method.
113 * @exception IllegalArgumentException if method is not supported.
114 * @see #STORED
115 * @see #DEFLATED
116 */
117 public void setMethod(int method)
118 {
119 if (method != STORED && method != DEFLATED)
120 throw new IllegalArgumentException("Method not supported.");
121 defaultMethod = method;
122 }
123
124 /**
125 * Sets default compression level. The new level will be activated
126 * immediately.
127 * @exception IllegalArgumentException if level is not supported.
128 * @see Deflater
129 */
130 public void setLevel(int level)
131 {
132 def.setLevel(level);
133 }
134
135 /**
136 * Write an unsigned short in little endian byte order.
137 */
138 private final void writeLeShort(int value) throws IOException
139 {
140 out.write(value & 0xff);
141 out.write((value >> 8) & 0xff);
142 }
143
144 /**
145 * Write an int in little endian byte order.
146 */
147 private final void writeLeInt(int value) throws IOException
148 {
149 writeLeShort(value);
150 writeLeShort(value >> 16);
151 }
152
153 /**
154 * Starts a new Zip entry. It automatically closes the previous
155 * entry if present. If the compression method is stored, the entry
156 * must have a valid size and crc, otherwise all elements (except
157 * name) are optional, but must be correct if present. If the time
158 * is not set in the entry, the current time is used.
159 * @param entry the entry.
160 * @exception IOException if an I/O error occured.
161 * @exception ZipException if stream was finished.
162 */
163 public void putNextEntry(ZipEntry entry) throws IOException
164 {
165 if (entries == null)
166 throw new ZipException("ZipOutputStream was finished");
167
168 int method = entry.getMethod();
169 int flags = 0;
170 if (method == -1)
171 method = defaultMethod;
172
173 if (method == STORED)
174 {
175 if (entry.getCompressedSize() >= 0)
176 {
177 if (entry.getSize() < 0)
178 entry.setSize(entry.getCompressedSize());
179 else if (entry.getSize() != entry.getCompressedSize())
180 throw new ZipException
181 ("Method STORED, but compressed size != size");
182 }
183 else
184 entry.setCompressedSize(entry.getSize());
185
186 if (entry.getSize() < 0)
187 throw new ZipException("Method STORED, but size not set");
188 if (entry.getCrc() < 0)
189 throw new ZipException("Method STORED, but crc not set");
190 }
191 else if (method == DEFLATED)
192 {
193 if (entry.getCompressedSize() < 0
194 || entry.getSize() < 0 || entry.getCrc() < 0)
195 flags |= 8;
196 }
197
198 if (curEntry != null)
199 closeEntry();
200
201 if (entry.getTime() < 0)
202 entry.setTime(System.currentTimeMillis());
203
204 entry.flags = flags;
205 entry.offset = offset;
206 entry.setMethod(method);
207 curMethod = method;
208 /* Write the local file header */
209 writeLeInt(LOCSIG);
210 writeLeShort(method == STORED
211 ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
212 writeLeShort(flags);
213 writeLeShort(method);
214 writeLeInt(entry.getDOSTime());
215 if ((flags & 8) == 0)
216 {
217 writeLeInt((int)entry.getCrc());
218 writeLeInt((int)entry.getCompressedSize());
219 writeLeInt((int)entry.getSize());
220 }
221 else
222 {
223 writeLeInt(0);
224 writeLeInt(0);
225 writeLeInt(0);
226 }
227 byte[] name = entry.getName().getBytes();
228 if (name.length > 0xffff)
229 throw new ZipException("Name too long.");
230 byte[] extra = entry.getExtra();
231 if (extra == null)
232 extra = new byte[0];
233 writeLeShort(name.length);
234 writeLeShort(extra.length);
235 out.write(name);
236 out.write(extra);
237
238 offset += LOCHDR + name.length + extra.length;
239
240 /* Activate the entry. */
241
242 curEntry = entry;
243 crc.reset();
244 if (method == DEFLATED)
245 def.reset();
246 size = 0;
247 }
248
249 /**
250 * Closes the current entry.
251 * @exception IOException if an I/O error occured.
252 * @exception ZipException if no entry is active.
253 */
254 public void closeEntry() throws IOException
255 {
256 if (curEntry == null)
257 throw new ZipException("No open entry");
258
259 /* First finish the deflater, if appropriate */
260 if (curMethod == DEFLATED)
261 super.finish();
262
263 int csize = curMethod == DEFLATED ? def.getTotalOut() : size;
264
265 if (curEntry.getSize() < 0)
266 curEntry.setSize(size);
267 else if (curEntry.getSize() != size)
268 throw new ZipException("size was "+size
269 +", but I expected "+curEntry.getSize());
270
271 if (curEntry.getCompressedSize() < 0)
272 curEntry.setCompressedSize(csize);
273 else if (curEntry.getCompressedSize() != csize)
274 throw new ZipException("compressed size was "+csize
275 +", but I expected "+curEntry.getSize());
276
277 if (curEntry.getCrc() < 0)
278 curEntry.setCrc(crc.getValue());
279 else if (curEntry.getCrc() != crc.getValue())
280 throw new ZipException("crc was " + Long.toHexString(crc.getValue())
281 + ", but I expected "
282 + Long.toHexString(curEntry.getCrc()));
283
284 offset += csize;
285
286 /* Now write the data descriptor entry if needed. */
287 if (curMethod == DEFLATED && (curEntry.flags & 8) != 0)
288 {
289 writeLeInt(EXTSIG);
290 writeLeInt((int)curEntry.getCrc());
291 writeLeInt((int)curEntry.getCompressedSize());
292 writeLeInt((int)curEntry.getSize());
293 offset += EXTHDR;
294 }
295
296 entries.addElement(curEntry);
297 curEntry = null;
298 }
299
300 /**
301 * Writes the given buffer to the current entry.
302 * @exception IOException if an I/O error occured.
303 * @exception ZipException if no entry is active.
304 */
305 public void write(byte[] b, int off, int len) throws IOException
306 {
307 if (curEntry == null)
308 throw new ZipException("No open entry.");
309
310 switch (curMethod)
311 {
312 case DEFLATED:
313 super.write(b, off, len);
314 break;
315
316 case STORED:
317 out.write(b, off, len);
318 break;
319 }
320
321 crc.update(b, off, len);
322 size += len;
323 }
324
325 /**
326 * Finishes the stream. This will write the central directory at the
327 * end of the zip file and flush the stream.
328 * @exception IOException if an I/O error occured.
329 */
330 public void finish() throws IOException
331 {
332 if (entries == null)
333 return;
334 if (curEntry != null)
335 closeEntry();
336
337 int numEntries = 0;
338 int sizeEntries = 0;
339
340 Enumeration enum = entries.elements();
341 while (enum.hasMoreElements())
342 {
343 ZipEntry entry = (ZipEntry) enum.nextElement();
344
345 int method = entry.getMethod();
346 writeLeInt(CENSIG);
347 writeLeShort(method == STORED
348 ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
349 writeLeShort(method == STORED
350 ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
351 writeLeShort(entry.flags);
352 writeLeShort(method);
353 writeLeInt(entry.getDOSTime());
354 writeLeInt((int)entry.getCrc());
355 writeLeInt((int)entry.getCompressedSize());
356 writeLeInt((int)entry.getSize());
357
358 byte[] name = entry.getName().getBytes();
359 if (name.length > 0xffff)
360 throw new ZipException("Name too long.");
361 byte[] extra = entry.getExtra();
362 if (extra == null)
363 extra = new byte[0];
364 String strComment = entry.getComment();
365 byte[] comment = strComment != null
366 ? strComment.getBytes() : new byte[0];
367 if (comment.length > 0xffff)
368 throw new ZipException("Comment too long.");
369
370 writeLeShort(name.length);
371 writeLeShort(extra.length);
372 writeLeShort(comment.length);
373 writeLeShort(0); /* disk number */
374 writeLeShort(0); /* internal file attr */
375 writeLeInt(0); /* external file attr */
376 writeLeInt(entry.offset);
377
378 out.write(name);
379 out.write(extra);
380 out.write(comment);
381 numEntries++;
382 sizeEntries += CENHDR + name.length + extra.length + comment.length;
383 }
384
385 writeLeInt(ENDSIG);
386 writeLeShort(0); /* disk number */
387 writeLeShort(0); /* disk with start of central dir */
388 writeLeShort(numEntries);
389 writeLeShort(numEntries);
390 writeLeInt(sizeEntries);
391 writeLeInt(offset);
392 writeLeShort(zipComment.length);
393 out.write(zipComment);
394 out.flush();
395 entries = null;
396 }
397}
Note: See TracBrowser for help on using the repository browser.