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