2 * Licensed to the Apache Software Foundation (ASF) under one or more
\r
3 * contributor license agreements. See the NOTICE file distributed with
\r
4 * this work for additional information regarding copyright ownership.
\r
5 * The ASF licenses this file to You under the Apache License, Version 2.0
\r
6 * (the "License"); you may not use this file except in compliance with
\r
7 * the License. You may obtain a copy of the License at
\r
9 * http://www.apache.org/licenses/LICENSE-2.0
\r
11 * Unless required by applicable law or agreed to in writing, software
\r
12 * distributed under the License is distributed on an "AS IS" BASIS,
\r
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
14 * See the License for the specific language governing permissions and
\r
15 * limitations under the License.
\r
19 package org.apache.tools.zip;
\r
21 import java.io.File;
\r
22 import java.io.FileOutputStream;
\r
23 import java.io.FilterOutputStream;
\r
24 import java.io.IOException;
\r
25 import java.io.OutputStream;
\r
26 import java.io.RandomAccessFile;
\r
27 import java.nio.ByteBuffer;
\r
28 import java.util.Date;
\r
29 import java.util.HashMap;
\r
30 import java.util.Iterator;
\r
31 import java.util.LinkedList;
\r
32 import java.util.List;
\r
33 import java.util.Map;
\r
34 import java.util.zip.CRC32;
\r
35 import java.util.zip.Deflater;
\r
36 import java.util.zip.ZipException;
\r
39 * Reimplementation of {@link java.util.zip.ZipOutputStream
\r
40 * java.util.zip.ZipOutputStream} that does handle the extended
\r
41 * functionality of this package, especially internal/external file
\r
42 * attributes and extra fields with different layouts for local file
\r
43 * data and central directory entries.
\r
45 * <p>This class will try to use {@link java.io.RandomAccessFile
\r
46 * RandomAccessFile} when you know that the output is going to go to a
\r
49 * <p>If RandomAccessFile cannot be used, this implementation will use
\r
50 * a Data Descriptor to store size and CRC information for {@link
\r
51 * #DEFLATED DEFLATED} entries, this means, you don't need to
\r
52 * calculate them yourself. Unfortunately this is not possible for
\r
53 * the {@link #STORED STORED} method, here setting the CRC and
\r
54 * uncompressed size information is required before {@link
\r
55 * #putNextEntry putNextEntry} can be called.</p>
\r
58 @SuppressWarnings({"unchecked", "rawtypes"})
\r
59 public class ZipOutputStream extends FilterOutputStream {
\r
61 private static final int BYTE_MASK = 0xFF;
\r
62 private static final int SHORT = 2;
\r
63 private static final int WORD = 4;
\r
64 private static final int BUFFER_SIZE = 512;
\r
66 * Apparently Deflater.setInput gets slowed down a lot on Sun JVMs
\r
67 * when it gets handed a really big buffer. See
\r
68 * https://issues.apache.org/bugzilla/show_bug.cgi?id=45396
\r
70 * Using a buffer size of 8 kB proved to be a good compromise
\r
72 private static final int DEFLATER_BLOCK_SIZE = 8192;
\r
75 * Compression method for deflated entries.
\r
79 public static final int DEFLATED = java.util.zip.ZipEntry.DEFLATED;
\r
82 * Default compression level for deflated entries.
\r
86 public static final int DEFAULT_COMPRESSION = Deflater.DEFAULT_COMPRESSION;
\r
89 * Compression method for stored entries.
\r
93 public static final int STORED = java.util.zip.ZipEntry.STORED;
\r
96 * default encoding for file names and comment.
\r
98 static final String DEFAULT_ENCODING = null;
\r
101 * General purpose flag, which indicates that filenames are
\r
102 * written in utf-8.
\r
104 public static final int UFT8_NAMES_FLAG = 1 << 11;
\r
107 * General purpose flag, which indicates that filenames are
\r
108 * written in utf-8.
\r
109 * @deprecated use {@link #UFT8_NAMES_FLAG} instead
\r
111 public static final int EFS_FLAG = UFT8_NAMES_FLAG;
\r
118 private ZipEntry entry;
\r
121 * The file comment.
\r
125 private String comment = "";
\r
128 * Compression level for next entry.
\r
132 private int level = DEFAULT_COMPRESSION;
\r
135 * Has the compression level changed when compared to the last
\r
140 private boolean hasCompressionLevelChanged = false;
\r
143 * Default compression method for next entry.
\r
147 private int method = java.util.zip.ZipEntry.DEFLATED;
\r
150 * List of ZipEntries written so far.
\r
154 private final List entries = new LinkedList();
\r
157 * CRC instance to avoid parsing DEFLATED data twice.
\r
161 private final CRC32 crc = new CRC32();
\r
164 * Count the bytes written to out.
\r
168 private long written = 0;
\r
171 * Data for local header data
\r
175 private long dataStart = 0;
\r
178 * Offset for CRC entry in the local file header data for the
\r
179 * current entry starts here.
\r
183 private long localDataStart = 0;
\r
186 * Start of central directory.
\r
190 private long cdOffset = 0;
\r
193 * Length of central directory.
\r
197 private long cdLength = 0;
\r
200 * Helper, a 0 as ZipShort.
\r
204 private static final byte[] ZERO = {0, 0};
\r
207 * Helper, a 0 as ZipLong.
\r
211 private static final byte[] LZERO = {0, 0, 0, 0};
\r
214 * Holds the offsets of the LFH starts for each entry.
\r
218 private final Map offsets = new HashMap();
\r
221 * The encoding to use for filenames and the file comment.
\r
223 * <p>For a list of possible values see <a
\r
224 * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>.
\r
225 * Defaults to the platform's default character encoding.</p>
\r
229 private String encoding = null;
\r
232 * The zip encoding to use for filenames and the file comment.
\r
234 * This field is of internal use and will be set in {@link
\r
235 * #setEncoding(String)}.
\r
237 private ZipEncoding zipEncoding =
\r
238 ZipEncodingHelper.getZipEncoding(DEFAULT_ENCODING);
\r
240 // CheckStyle:VisibilityModifier OFF - bc
\r
243 * This Deflater object is used for output.
\r
245 * <p>This attribute is only protected to provide a level of API
\r
246 * backwards compatibility. This class used to extend {@link
\r
247 * java.util.zip.DeflaterOutputStream DeflaterOutputStream} up to
\r
248 * Revision 1.13.</p>
\r
252 protected Deflater def = new Deflater(level, true);
\r
255 * This buffer servers as a Deflater.
\r
257 * <p>This attribute is only protected to provide a level of API
\r
258 * backwards compatibility. This class used to extend {@link
\r
259 * java.util.zip.DeflaterOutputStream DeflaterOutputStream} up to
\r
260 * Revision 1.13.</p>
\r
264 protected byte[] buf = new byte[BUFFER_SIZE];
\r
266 // CheckStyle:VisibilityModifier ON
\r
269 * Optional random access output.
\r
273 private RandomAccessFile raf = null;
\r
276 * whether to use the general purpose bit flag when writing UTF-8
\r
277 * filenames or not.
\r
279 private boolean useUTF8Flag = true;
\r
282 * Whether to encode non-encodable file names as UTF-8.
\r
284 private boolean fallbackToUTF8 = false;
\r
287 * whether to create UnicodePathExtraField-s for each entry.
\r
289 private UnicodeExtraFieldPolicy createUnicodeExtraFields =
\r
290 UnicodeExtraFieldPolicy.NEVER;
\r
293 * Creates a new ZIP OutputStream filtering the underlying stream.
\r
294 * @param out the outputstream to zip
\r
297 public ZipOutputStream(OutputStream out) {
\r
302 * Creates a new ZIP OutputStream writing to a File. Will use
\r
303 * random access if possible.
\r
304 * @param file the file to zip to
\r
306 * @throws IOException on error
\r
308 public ZipOutputStream(File file) throws IOException {
\r
312 raf = new RandomAccessFile(file, "rw");
\r
314 } catch (IOException e) {
\r
318 } catch (IOException inner) {
\r
323 out = new FileOutputStream(file);
\r
328 * This method indicates whether this archive is writing to a
\r
329 * seekable stream (i.e., to a random access file).
\r
331 * <p>For seekable streams, you don't need to calculate the CRC or
\r
332 * uncompressed size for {@link #STORED} entries before
\r
333 * invoking {@link #putNextEntry}.
\r
334 * @return true if seekable
\r
337 public boolean isSeekable() {
\r
338 return raf != null;
\r
342 * The encoding to use for filenames and the file comment.
\r
344 * <p>For a list of possible values see <a
\r
345 * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>.
\r
346 * Defaults to the platform's default character encoding.</p>
\r
347 * @param encoding the encoding value
\r
350 public void setEncoding(final String encoding) {
\r
351 this.encoding = encoding;
\r
352 this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
\r
353 useUTF8Flag &= ZipEncodingHelper.isUTF8(encoding);
\r
357 * The encoding to use for filenames and the file comment.
\r
359 * @return null if using the platform's default character encoding.
\r
363 public String getEncoding() {
\r
368 * Whether to set the language encoding flag if the file name
\r
369 * encoding is UTF-8.
\r
371 * <p>Defaults to true.</p>
\r
373 public void setUseLanguageEncodingFlag(boolean b) {
\r
374 useUTF8Flag = b && ZipEncodingHelper.isUTF8(encoding);
\r
378 * Whether to create Unicode Extra Fields.
\r
380 * <p>Defaults to NEVER.</p>
\r
382 public void setCreateUnicodeExtraFields(UnicodeExtraFieldPolicy b) {
\r
383 createUnicodeExtraFields = b;
\r
387 * Whether to fall back to UTF and the language encoding flag if
\r
388 * the file name cannot be encoded using the specified encoding.
\r
390 * <p>Defaults to false.</p>
\r
392 public void setFallbackToUTF8(boolean b) {
\r
393 fallbackToUTF8 = b;
\r
397 * Finishs writing the contents and closes this as well as the
\r
398 * underlying stream.
\r
401 * @throws IOException on error
\r
403 public void finish() throws IOException {
\r
405 cdOffset = written;
\r
406 for (Iterator i = entries.iterator(); i.hasNext(); ) {
\r
407 writeCentralFileHeader((ZipEntry) i.next());
\r
409 cdLength = written - cdOffset;
\r
410 writeCentralDirectoryEnd();
\r
416 * Writes all necessary data for this entry.
\r
419 * @throws IOException on error
\r
421 public void closeEntry() throws IOException {
\r
422 if (entry == null) {
\r
426 long realCrc = crc.getValue();
\r
429 if (entry.getMethod() == DEFLATED) {
\r
431 while (!def.finished()) {
\r
435 entry.setSize(adjustToLong(def.getTotalIn()));
\r
436 entry.setCompressedSize(adjustToLong(def.getTotalOut()));
\r
437 entry.setCrc(realCrc);
\r
441 written += entry.getCompressedSize();
\r
442 } else if (raf == null) {
\r
443 if (entry.getCrc() != realCrc) {
\r
444 throw new ZipException("bad CRC checksum for entry "
\r
445 + entry.getName() + ": "
\r
446 + Long.toHexString(entry.getCrc())
\r
448 + Long.toHexString(realCrc));
\r
451 if (entry.getSize() != written - dataStart) {
\r
452 throw new ZipException("bad size for entry "
\r
453 + entry.getName() + ": "
\r
456 + (written - dataStart));
\r
458 } else { /* method is STORED and we used RandomAccessFile */
\r
459 long size = written - dataStart;
\r
461 entry.setSize(size);
\r
462 entry.setCompressedSize(size);
\r
463 entry.setCrc(realCrc);
\r
466 // If random access output, write the local file header containing
\r
467 // the correct CRC and compressed/uncompressed sizes
\r
469 long save = raf.getFilePointer();
\r
471 raf.seek(localDataStart);
\r
472 writeOut(ZipLong.getBytes(entry.getCrc()));
\r
473 writeOut(ZipLong.getBytes(entry.getCompressedSize()));
\r
474 writeOut(ZipLong.getBytes(entry.getSize()));
\r
478 writeDataDescriptor(entry);
\r
483 * Begin writing next entry.
\r
484 * @param ze the entry to write
\r
486 * @throws IOException on error
\r
488 public void putNextEntry(ZipEntry ze) throws IOException {
\r
492 entries.add(entry);
\r
494 if (entry.getMethod() == -1) { // not specified
\r
495 entry.setMethod(method);
\r
498 if (entry.getTime() == -1) { // not specified
\r
499 entry.setTime(System.currentTimeMillis());
\r
502 // Size/CRC not required if RandomAccessFile is used
\r
503 if (entry.getMethod() == STORED && raf == null) {
\r
504 if (entry.getSize() == -1) {
\r
505 throw new ZipException("uncompressed size is required for"
\r
506 + " STORED method when not writing to a"
\r
509 if (entry.getCrc() == -1) {
\r
510 throw new ZipException("crc checksum is required for STORED"
\r
511 + " method when not writing to a file");
\r
513 entry.setCompressedSize(entry.getSize());
\r
516 if (entry.getMethod() == DEFLATED && hasCompressionLevelChanged) {
\r
517 def.setLevel(level);
\r
518 hasCompressionLevelChanged = false;
\r
520 writeLocalFileHeader(entry);
\r
524 * Set the file comment.
\r
525 * @param comment the comment
\r
528 public void setComment(String comment) {
\r
529 this.comment = comment;
\r
533 * Sets the compression level for subsequent entries.
\r
535 * <p>Default is Deflater.DEFAULT_COMPRESSION.</p>
\r
536 * @param level the compression level.
\r
537 * @throws IllegalArgumentException if an invalid compression
\r
538 * level is specified.
\r
541 public void setLevel(int level) {
\r
542 if (level < Deflater.DEFAULT_COMPRESSION
\r
543 || level > Deflater.BEST_COMPRESSION) {
\r
544 throw new IllegalArgumentException("Invalid compression level: "
\r
547 hasCompressionLevelChanged = (this.level != level);
\r
548 this.level = level;
\r
552 * Sets the default compression method for subsequent entries.
\r
554 * <p>Default is DEFLATED.</p>
\r
555 * @param method an <code>int</code> from java.util.zip.ZipEntry
\r
558 public void setMethod(int method) {
\r
559 this.method = method;
\r
563 * Writes bytes to ZIP entry.
\r
564 * @param b the byte array to write
\r
565 * @param offset the start position to write from
\r
566 * @param length the number of bytes to write
\r
567 * @throws IOException on error
\r
569 public void write(byte[] b, int offset, int length) throws IOException {
\r
570 if (entry.getMethod() == DEFLATED) {
\r
572 if (!def.finished()) {
\r
573 if (length <= DEFLATER_BLOCK_SIZE) {
\r
574 def.setInput(b, offset, length);
\r
575 deflateUntilInputIsNeeded();
\r
577 final int fullblocks = length / DEFLATER_BLOCK_SIZE;
\r
578 for (int i = 0; i < fullblocks; i++) {
\r
579 def.setInput(b, offset + i * DEFLATER_BLOCK_SIZE,
\r
580 DEFLATER_BLOCK_SIZE);
\r
581 deflateUntilInputIsNeeded();
\r
583 final int done = fullblocks * DEFLATER_BLOCK_SIZE;
\r
584 if (done < length) {
\r
585 def.setInput(b, offset + done, length - done);
\r
586 deflateUntilInputIsNeeded();
\r
592 writeOut(b, offset, length);
\r
595 crc.update(b, offset, length);
\r
599 * Writes a single byte to ZIP entry.
\r
601 * <p>Delegates to the three arg method.</p>
\r
602 * @param b the byte to write
\r
604 * @throws IOException on error
\r
606 public void write(int b) throws IOException {
\r
607 byte[] buff = new byte[1];
\r
608 buff[0] = (byte) (b & BYTE_MASK);
\r
613 * Closes this output stream and releases any system resources
\r
614 * associated with the stream.
\r
616 * @exception IOException if an I/O error occurs.
\r
619 public void close() throws IOException {
\r
631 * Flushes this output stream and forces any buffered output bytes
\r
632 * to be written out to the stream.
\r
634 * @exception IOException if an I/O error occurs.
\r
637 public void flush() throws IOException {
\r
644 * Various ZIP constants
\r
647 * local file header signature
\r
651 protected static final byte[] LFH_SIG = ZipLong.getBytes(0X04034B50L);
\r
653 * data descriptor signature
\r
657 protected static final byte[] DD_SIG = ZipLong.getBytes(0X08074B50L);
\r
659 * central file header signature
\r
663 protected static final byte[] CFH_SIG = ZipLong.getBytes(0X02014B50L);
\r
665 * end of central dir signature
\r
669 protected static final byte[] EOCD_SIG = ZipLong.getBytes(0X06054B50L);
\r
672 * Writes next block of compressed data to the output stream.
\r
673 * @throws IOException on error
\r
677 protected final void deflate() throws IOException {
\r
678 int len = def.deflate(buf, 0, buf.length);
\r
680 writeOut(buf, 0, len);
\r
685 * Writes the local file header entry
\r
686 * @param ze the entry to write
\r
687 * @throws IOException on error
\r
691 protected void writeLocalFileHeader(ZipEntry ze) throws IOException {
\r
693 boolean encodable = zipEncoding.canEncode(ze.getName());
\r
695 final ZipEncoding entryEncoding;
\r
697 if (!encodable && fallbackToUTF8) {
\r
698 entryEncoding = ZipEncodingHelper.UTF8_ZIP_ENCODING;
\r
700 entryEncoding = zipEncoding;
\r
703 ByteBuffer name = entryEncoding.encode(ze.getName());
\r
705 if (createUnicodeExtraFields != UnicodeExtraFieldPolicy.NEVER) {
\r
707 if (createUnicodeExtraFields == UnicodeExtraFieldPolicy.ALWAYS
\r
709 ze.addExtraField(new UnicodePathExtraField(ze.getName(),
\r
711 name.arrayOffset(),
\r
715 String comm = ze.getComment();
\r
716 if (comm != null && !"".equals(comm)) {
\r
718 boolean commentEncodable = this.zipEncoding.canEncode(comm);
\r
720 if (createUnicodeExtraFields == UnicodeExtraFieldPolicy.ALWAYS
\r
721 || !commentEncodable) {
\r
722 ByteBuffer commentB = entryEncoding.encode(comm);
\r
723 ze.addExtraField(new UnicodeCommentExtraField(comm,
\r
725 commentB.arrayOffset(),
\r
732 offsets.put(ze, ZipLong.getBytes(written));
\r
737 //store method in local variable to prevent multiple method calls
\r
738 final int zipMethod = ze.getMethod();
\r
740 writeVersionNeededToExtractAndGeneralPurposeBits(zipMethod,
\r
742 && fallbackToUTF8);
\r
745 // compression method
\r
746 writeOut(ZipShort.getBytes(zipMethod));
\r
749 // last mod. time and date
\r
750 writeOut(toDosTime(ze.getTime()));
\r
754 // compressed length
\r
755 // uncompressed length
\r
756 localDataStart = written;
\r
757 if (zipMethod == DEFLATED || raf != null) {
\r
762 writeOut(ZipLong.getBytes(ze.getCrc()));
\r
763 writeOut(ZipLong.getBytes(ze.getSize()));
\r
764 writeOut(ZipLong.getBytes(ze.getSize()));
\r
766 // CheckStyle:MagicNumber OFF
\r
768 // CheckStyle:MagicNumber ON
\r
770 // file name length
\r
771 writeOut(ZipShort.getBytes(name.limit()));
\r
774 // extra field length
\r
775 byte[] extra = ze.getLocalFileDataExtra();
\r
776 writeOut(ZipShort.getBytes(extra.length));
\r
780 writeOut(name.array(), name.arrayOffset(), name.limit());
\r
781 written += name.limit();
\r
785 written += extra.length;
\r
787 dataStart = written;
\r
791 * Writes the data descriptor entry.
\r
792 * @param ze the entry to write
\r
793 * @throws IOException on error
\r
797 protected void writeDataDescriptor(ZipEntry ze) throws IOException {
\r
798 if (ze.getMethod() != DEFLATED || raf != null) {
\r
802 writeOut(ZipLong.getBytes(entry.getCrc()));
\r
803 writeOut(ZipLong.getBytes(entry.getCompressedSize()));
\r
804 writeOut(ZipLong.getBytes(entry.getSize()));
\r
805 // CheckStyle:MagicNumber OFF
\r
807 // CheckStyle:MagicNumber ON
\r
811 * Writes the central file header entry.
\r
812 * @param ze the entry to write
\r
813 * @throws IOException on error
\r
817 protected void writeCentralFileHeader(ZipEntry ze) throws IOException {
\r
822 // CheckStyle:MagicNumber OFF
\r
823 writeOut(ZipShort.getBytes((ze.getPlatform() << 8) | 20));
\r
826 final int zipMethod = ze.getMethod();
\r
827 final boolean encodable = zipEncoding.canEncode(ze.getName());
\r
828 writeVersionNeededToExtractAndGeneralPurposeBits(zipMethod,
\r
830 && fallbackToUTF8);
\r
833 // compression method
\r
834 writeOut(ZipShort.getBytes(zipMethod));
\r
837 // last mod. time and date
\r
838 writeOut(toDosTime(ze.getTime()));
\r
842 // compressed length
\r
843 // uncompressed length
\r
844 writeOut(ZipLong.getBytes(ze.getCrc()));
\r
845 writeOut(ZipLong.getBytes(ze.getCompressedSize()));
\r
846 writeOut(ZipLong.getBytes(ze.getSize()));
\r
847 // CheckStyle:MagicNumber OFF
\r
849 // CheckStyle:MagicNumber ON
\r
851 // file name length
\r
852 final ZipEncoding entryEncoding;
\r
854 if (!encodable && fallbackToUTF8) {
\r
855 entryEncoding = ZipEncodingHelper.UTF8_ZIP_ENCODING;
\r
857 entryEncoding = zipEncoding;
\r
860 ByteBuffer name = entryEncoding.encode(ze.getName());
\r
862 writeOut(ZipShort.getBytes(name.limit()));
\r
865 // extra field length
\r
866 byte[] extra = ze.getCentralDirectoryExtra();
\r
867 writeOut(ZipShort.getBytes(extra.length));
\r
870 // file comment length
\r
871 String comm = ze.getComment();
\r
872 if (comm == null) {
\r
876 ByteBuffer commentB = entryEncoding.encode(comm);
\r
878 writeOut(ZipShort.getBytes(commentB.limit()));
\r
881 // disk number start
\r
885 // internal file attributes
\r
886 writeOut(ZipShort.getBytes(ze.getInternalAttributes()));
\r
889 // external file attributes
\r
890 writeOut(ZipLong.getBytes(ze.getExternalAttributes()));
\r
893 // relative offset of LFH
\r
894 writeOut((byte[]) offsets.get(ze));
\r
898 writeOut(name.array(), name.arrayOffset(), name.limit());
\r
899 written += name.limit();
\r
903 written += extra.length;
\r
906 writeOut(commentB.array(), commentB.arrayOffset(), commentB.limit());
\r
907 written += commentB.limit();
\r
911 * Writes the "End of central dir record".
\r
912 * @throws IOException on error
\r
916 protected void writeCentralDirectoryEnd() throws IOException {
\r
917 writeOut(EOCD_SIG);
\r
923 // number of entries
\r
924 byte[] num = ZipShort.getBytes(entries.size());
\r
928 // length and location of CD
\r
929 writeOut(ZipLong.getBytes(cdLength));
\r
930 writeOut(ZipLong.getBytes(cdOffset));
\r
932 // ZIP file comment
\r
933 ByteBuffer data = this.zipEncoding.encode(comment);
\r
934 writeOut(ZipShort.getBytes(data.limit()));
\r
935 writeOut(data.array(), data.arrayOffset(), data.limit());
\r
939 * Smallest date/time ZIP can handle.
\r
943 private static final byte[] DOS_TIME_MIN = ZipLong.getBytes(0x00002100L);
\r
946 * Convert a Date object to a DOS date/time field.
\r
947 * @param time the <code>Date</code> to convert
\r
948 * @return the date as a <code>ZipLong</code>
\r
951 protected static ZipLong toDosTime(Date time) {
\r
952 return new ZipLong(toDosTime(time.getTime()));
\r
956 * Convert a Date object to a DOS date/time field.
\r
958 * <p>Stolen from InfoZip's <code>fileio.c</code></p>
\r
959 * @param t number of milliseconds since the epoch
\r
960 * @return the date as a byte array
\r
963 @SuppressWarnings("deprecation")
\r
964 protected static byte[] toDosTime(long t) {
\r
965 Date time = new Date(t);
\r
966 // CheckStyle:MagicNumberCheck OFF - I do not think that using constants
\r
967 // here will improve the readablity
\r
968 int year = time.getYear() + 1900;
\r
970 return DOS_TIME_MIN;
\r
972 int month = time.getMonth() + 1;
\r
973 long value = ((year - 1980) << 25)
\r
975 | (time.getDate() << 16)
\r
976 | (time.getHours() << 11)
\r
977 | (time.getMinutes() << 5)
\r
978 | (time.getSeconds() >> 1);
\r
979 return ZipLong.getBytes(value);
\r
980 // CheckStyle:MagicNumberCheck ON
\r
984 * Retrieve the bytes for the given String in the encoding set for
\r
986 * @param name the string to get bytes from
\r
987 * @return the bytes as a byte array
\r
988 * @throws ZipException on error
\r
992 protected byte[] getBytes(String name) throws ZipException {
\r
995 ZipEncodingHelper.getZipEncoding(encoding).encode(name);
\r
996 byte[] result = new byte[b.limit()];
\r
997 System.arraycopy(b.array(), b.arrayOffset(), result, 0,
\r
1000 } catch (IOException ex) {
\r
1001 throw new ZipException("Failed to encode name: " + ex.getMessage());
\r
1006 * Write bytes to output or random access file.
\r
1007 * @param data the byte array to write
\r
1008 * @throws IOException on error
\r
1012 protected final void writeOut(byte[] data) throws IOException {
\r
1013 writeOut(data, 0, data.length);
\r
1017 * Write bytes to output or random access file.
\r
1018 * @param data the byte array to write
\r
1019 * @param offset the start position to write from
\r
1020 * @param length the number of bytes to write
\r
1021 * @throws IOException on error
\r
1025 protected final void writeOut(byte[] data, int offset, int length)
\r
1026 throws IOException {
\r
1027 if (raf != null) {
\r
1028 raf.write(data, offset, length);
\r
1030 out.write(data, offset, length);
\r
1035 * Assumes a negative integer really is a positive integer that
\r
1036 * has wrapped around and re-creates the original value.
\r
1037 * @param i the value to treat as unsigned int.
\r
1038 * @return the unsigned int as a long.
\r
1041 protected static long adjustToLong(int i) {
\r
1043 return 2 * ((long) Integer.MAX_VALUE) + 2 + i;
\r
1049 private void deflateUntilInputIsNeeded() throws IOException {
\r
1050 while (!def.needsInput()) {
\r
1055 private void writeVersionNeededToExtractAndGeneralPurposeBits(final int
\r
1059 throws IOException {
\r
1061 // CheckStyle:MagicNumber OFF
\r
1062 int versionNeededToExtract = 10;
\r
1063 int generalPurposeFlag = (useUTF8Flag || utfFallback) ? UFT8_NAMES_FLAG : 0;
\r
1064 if (zipMethod == DEFLATED && raf == null) {
\r
1065 // requires version 2 as we are going to store length info
\r
1066 // in the data descriptor
\r
1067 versionNeededToExtract = 20;
\r
1068 // bit3 set to signal, we use a data descriptor
\r
1069 generalPurposeFlag |= 8;
\r
1071 // CheckStyle:MagicNumber ON
\r
1073 // version needed to extract
\r
1074 writeOut(ZipShort.getBytes(versionNeededToExtract));
\r
1075 // general purpose bit flag
\r
1076 writeOut(ZipShort.getBytes(generalPurposeFlag));
\r
1080 * enum that represents the possible policies for creating Unicode
\r
1083 public static final class UnicodeExtraFieldPolicy {
\r
1085 * Always create Unicode extra fields.
\r
1087 public static final UnicodeExtraFieldPolicy ALWAYS =
\r
1088 new UnicodeExtraFieldPolicy("always");
\r
1090 * Never create Unicode extra fields.
\r
1092 public static final UnicodeExtraFieldPolicy NEVER =
\r
1093 new UnicodeExtraFieldPolicy("never");
\r
1095 * Create Unicode extra fields for filenames that cannot be
\r
1096 * encoded using the specified encoding.
\r
1098 public static final UnicodeExtraFieldPolicy NOT_ENCODEABLE =
\r
1099 new UnicodeExtraFieldPolicy("not encodeable");
\r
1101 private final String name;
\r
1102 private UnicodeExtraFieldPolicy(String n) {
\r
1105 public String toString() {
\r