GNU Classpath merge.
[official-gcc.git] / libjava / java / util / zip / ZipOutputStream.java
blob44c4a9cc9b138288acf95ad98bd7b197c10533e8
1 /* java.util.zip.ZipOutputStream
2 Copyright (C) 2001 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. */
38 package java.util.zip;
39 import java.io.OutputStream;
40 import java.io.IOException;
41 import java.io.UnsupportedEncodingException;
42 import java.util.Vector;
43 import java.util.Enumeration;
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.
51 * It includes support for STORED and DEFLATED entries.
53 * This class is not thread safe.
55 * @author Jochen Hoenicke
57 public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants
59 private Vector entries = new Vector();
60 private CRC32 crc = new CRC32();
61 private ZipEntry curEntry = null;
63 private int curMethod;
64 private int size;
65 private int offset = 0;
67 private byte[] zipComment = new byte[0];
68 private int defaultMethod = DEFLATED;
70 /**
71 * Our Zip version is hard coded to 1.0 resp. 2.0
73 private final static int ZIP_STORED_VERSION = 10;
74 private final static int ZIP_DEFLATED_VERSION = 20;
76 /**
77 * Compression method. This method doesn't compress at all.
79 public final static int STORED = 0;
80 /**
81 * Compression method. This method uses the Deflater.
83 public final static int DEFLATED = 8;
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.
89 public ZipOutputStream(OutputStream out)
91 super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
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.
100 public void setComment(String comment)
102 byte[] commentBytes;
103 commentBytes = comment.getBytes();
104 if (commentBytes.length > 0xffff)
105 throw new IllegalArgumentException("Comment too long.");
106 zipComment = commentBytes;
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
117 public void setMethod(int method)
119 if (method != STORED && method != DEFLATED)
120 throw new IllegalArgumentException("Method not supported.");
121 defaultMethod = method;
125 * Sets default compression level. The new level will be activated
126 * immediately.
127 * @exception IllegalArgumentException if level is not supported.
128 * @see Deflater
130 public void setLevel(int level)
132 def.setLevel(level);
136 * Write an unsigned short in little endian byte order.
138 private final void writeLeShort(int value) throws IOException
140 out.write(value & 0xff);
141 out.write((value >> 8) & 0xff);
145 * Write an int in little endian byte order.
147 private final void writeLeInt(int value) throws IOException
149 writeLeShort(value);
150 writeLeShort(value >> 16);
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.
163 public void putNextEntry(ZipEntry entry) throws IOException
165 if (entries == null)
166 throw new ZipException("ZipOutputStream was finished");
168 int method = entry.getMethod();
169 int flags = 0;
170 if (method == -1)
171 method = defaultMethod;
173 if (method == STORED)
175 if (entry.getCompressedSize() >= 0)
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");
183 else
184 entry.setCompressedSize(entry.getSize());
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");
191 else if (method == DEFLATED)
193 if (entry.getCompressedSize() < 0
194 || entry.getSize() < 0 || entry.getCrc() < 0)
195 flags |= 8;
198 if (curEntry != null)
199 closeEntry();
201 if (entry.getTime() < 0)
202 entry.setTime(System.currentTimeMillis());
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)
217 writeLeInt((int)entry.getCrc());
218 writeLeInt((int)entry.getCompressedSize());
219 writeLeInt((int)entry.getSize());
221 else
223 writeLeInt(0);
224 writeLeInt(0);
225 writeLeInt(0);
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);
238 offset += LOCHDR + name.length + extra.length;
240 /* Activate the entry. */
242 curEntry = entry;
243 crc.reset();
244 if (method == DEFLATED)
245 def.reset();
246 size = 0;
250 * Closes the current entry.
251 * @exception IOException if an I/O error occured.
252 * @exception ZipException if no entry is active.
254 public void closeEntry() throws IOException
256 if (curEntry == null)
257 throw new ZipException("No open entry");
259 /* First finish the deflater, if appropriate */
260 if (curMethod == DEFLATED)
261 super.finish();
263 int csize = curMethod == DEFLATED ? def.getTotalOut() : size;
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());
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());
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()));
284 offset += csize;
286 /* Now write the data descriptor entry if needed. */
287 if (curMethod == DEFLATED && (curEntry.flags & 8) != 0)
289 writeLeInt(EXTSIG);
290 writeLeInt((int)curEntry.getCrc());
291 writeLeInt((int)curEntry.getCompressedSize());
292 writeLeInt((int)curEntry.getSize());
293 offset += EXTHDR;
296 entries.addElement(curEntry);
297 curEntry = null;
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.
305 public void write(byte[] b, int off, int len) throws IOException
307 if (curEntry == null)
308 throw new ZipException("No open entry.");
310 switch (curMethod)
312 case DEFLATED:
313 super.write(b, off, len);
314 break;
316 case STORED:
317 out.write(b, off, len);
318 break;
321 crc.update(b, off, len);
322 size += len;
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.
330 public void finish() throws IOException
332 if (entries == null)
333 return;
334 if (curEntry != null)
335 closeEntry();
337 int numEntries = 0;
338 int sizeEntries = 0;
340 Enumeration enum = entries.elements();
341 while (enum.hasMoreElements())
343 ZipEntry entry = (ZipEntry) enum.nextElement();
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());
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.");
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);
378 out.write(name);
379 out.write(extra);
380 out.write(comment);
381 numEntries++;
382 sizeEntries += CENHDR + name.length + extra.length + comment.length;
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;