ZipEntry.java revision 1a9ca631d5e2d44f39b556132af3a3cb4e34a966
1/*
2 * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.util.zip;
27
28import java.nio.charset.StandardCharsets;
29import java.util.Date;
30
31/**
32 * This class is used to represent a ZIP file entry.
33 *
34 * @author      David Connelly
35 */
36public
37class ZipEntry implements ZipConstants, Cloneable {
38    String name;        // entry name
39    long time = -1;     // modification time (in DOS time)
40    long crc = -1;      // crc-32 of entry data
41    long size = -1;     // uncompressed size of entry data
42    long csize = -1;    // compressed size of entry data
43    int method = -1;    // compression method
44    int flag = 0;       // general purpose flag
45    byte[] extra;       // optional extra field data for entry
46    String comment;     // optional comment string for entry
47    // Android-changed: Add dataOffset for internal use.
48    long dataOffset;
49
50    /**
51     * Compression method for uncompressed entries.
52     */
53    public static final int STORED = 0;
54
55    /**
56     * Compression method for compressed (deflated) entries.
57     */
58    public static final int DEFLATED = 8;
59
60
61    /** @hide - for testing only */
62    public ZipEntry(String name, String comment, long crc, long compressedSize,
63            long size, int compressionMethod, int time, byte[] extra,
64            long dataOffset) {
65        this.name = name;
66        this.comment = comment;
67        this.crc = crc;
68        this.csize = compressedSize;
69        this.size = size;
70        this.method = compressionMethod;
71        this.time = time;
72        this.extra = extra;
73        this.dataOffset = dataOffset;
74    }
75
76    /**
77     * Creates a new zip entry with the specified name.
78     *
79     * @param name the entry name
80     * @exception NullPointerException if the entry name is null
81     * @exception IllegalArgumentException if the entry name is longer than
82     *            0xFFFF bytes
83     */
84    public ZipEntry(String name) {
85        if (name == null) {
86            throw new NullPointerException();
87        }
88
89        // Android-changed: Explicitly use UTF_8 instead of the default charset.
90        if (name.getBytes(StandardCharsets.UTF_8).length > 0xffff) {
91            throw new IllegalArgumentException(name + " too long: " +
92                    name.getBytes(StandardCharsets.UTF_8).length);
93        }
94        this.name = name;
95    }
96
97    /**
98     * Creates a new zip entry with fields taken from the specified
99     * zip entry.
100     * @param e a zip Entry object
101     */
102    public ZipEntry(ZipEntry e) {
103        name = e.name;
104        time = e.time;
105        crc = e.crc;
106        size = e.size;
107        csize = e.csize;
108        method = e.method;
109        flag = e.flag;
110        extra = e.extra;
111        comment = e.comment;
112        dataOffset = e.dataOffset;
113    }
114
115    /*
116     * Creates a new un-initialized zip entry
117     */
118    ZipEntry() {}
119
120    /** @hide */
121    public long getDataOffset() {
122        return dataOffset;
123    }
124
125    /**
126     * Returns the name of the entry.
127     * @return the name of the entry
128     */
129    public String getName() {
130        return name;
131    }
132
133    /**
134     * Sets the modification time of the entry.
135     * @param time the entry modification time in number of milliseconds
136     *             since the epoch
137     * @see #getTime()
138     */
139    public void setTime(long time) {
140        this.time = javaToDosTime(time);
141    }
142
143    /**
144     * Returns the modification time of the entry, or -1 if not specified.
145     * @return the modification time of the entry, or -1 if not specified
146     * @see #setTime(long)
147     */
148    public long getTime() {
149        return time != -1 ? dosToJavaTime(time) : -1;
150    }
151
152    /**
153     * Sets the uncompressed size of the entry data.
154     * @param size the uncompressed size in bytes
155     * @exception IllegalArgumentException if the specified size is less
156     *            than 0, is greater than 0xFFFFFFFF when
157     *            <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
158     *            or is less than 0 when ZIP64 is supported
159     * @see #getSize()
160     */
161    public void setSize(long size) {
162        if (size < 0) {
163            throw new IllegalArgumentException("invalid entry size");
164        }
165        this.size = size;
166    }
167
168    /**
169     * Returns the uncompressed size of the entry data, or -1 if not known.
170     * @return the uncompressed size of the entry data, or -1 if not known
171     * @see #setSize(long)
172     */
173    public long getSize() {
174        return size;
175    }
176
177    /**
178     * Returns the size of the compressed entry data, or -1 if not known.
179     * In the case of a stored entry, the compressed size will be the same
180     * as the uncompressed size of the entry.
181     * @return the size of the compressed entry data, or -1 if not known
182     * @see #setCompressedSize(long)
183     */
184    public long getCompressedSize() {
185        return csize;
186    }
187
188    /**
189     * Sets the size of the compressed entry data.
190     * @param csize the compressed size to set to
191     * @see #getCompressedSize()
192     */
193    public void setCompressedSize(long csize) {
194        this.csize = csize;
195    }
196
197    /**
198     * Sets the CRC-32 checksum of the uncompressed entry data.
199     * @param crc the CRC-32 value
200     * @exception IllegalArgumentException if the specified CRC-32 value is
201     *            less than 0 or greater than 0xFFFFFFFF
202     * @see #getCrc()
203     */
204    public void setCrc(long crc) {
205        if (crc < 0 || crc > 0xFFFFFFFFL) {
206            throw new IllegalArgumentException("invalid entry crc-32");
207        }
208        this.crc = crc;
209    }
210
211    /**
212     * Returns the CRC-32 checksum of the uncompressed entry data, or -1 if
213     * not known.
214     * @return the CRC-32 checksum of the uncompressed entry data, or -1 if
215     * not known
216     * @see #setCrc(long)
217     */
218    public long getCrc() {
219        return crc;
220    }
221
222    /**
223     * Sets the compression method for the entry.
224     * @param method the compression method, either STORED or DEFLATED
225     * @exception IllegalArgumentException if the specified compression
226     *            method is invalid
227     * @see #getMethod()
228     */
229    public void setMethod(int method) {
230        if (method != STORED && method != DEFLATED) {
231            throw new IllegalArgumentException("invalid compression method");
232        }
233        this.method = method;
234    }
235
236    /**
237     * Returns the compression method of the entry, or -1 if not specified.
238     * @return the compression method of the entry, or -1 if not specified
239     * @see #setMethod(int)
240     */
241    public int getMethod() {
242        return method;
243    }
244
245    /**
246     * Sets the optional extra field data for the entry.
247     * @param extra the extra field data bytes
248     * @exception IllegalArgumentException if the length of the specified
249     *            extra field data is greater than 0xFFFF bytes
250     * @see #getExtra()
251     */
252    public void setExtra(byte[] extra) {
253        if (extra != null && extra.length > 0xFFFF) {
254            throw new IllegalArgumentException("invalid extra field length");
255        }
256        this.extra = extra;
257    }
258
259    /**
260     * Returns the extra field data for the entry, or null if none.
261     * @return the extra field data for the entry, or null if none
262     * @see #setExtra(byte[])
263     */
264    public byte[] getExtra() {
265        return extra;
266    }
267
268    /**
269     * Sets the optional comment string for the entry.
270     *
271     * <p>ZIP entry comments have maximum length of 0xffff. If the length of the
272     * specified comment string is greater than 0xFFFF bytes after encoding, only
273     * the first 0xFFFF bytes are output to the ZIP file entry.
274     *
275     * @param comment the comment string
276     *
277     * @see #getComment()
278     */
279    public void setComment(String comment) {
280        // Android-changed: Explicitly allow null comments (or allow comments to be
281        // cleared).
282        if (comment == null) {
283            this.comment = null;
284            return;
285        }
286
287        // Android-changed: Explicitly use UTF-8.
288        if (comment.getBytes(StandardCharsets.UTF_8).length > 0xffff) {
289            throw new IllegalArgumentException(comment + " too long: " +
290                    comment.getBytes(StandardCharsets.UTF_8).length);
291        }
292        this.comment = comment;
293    }
294
295    /**
296     * Returns the comment string for the entry, or null if none.
297     * @return the comment string for the entry, or null if none
298     * @see #setComment(String)
299     */
300    public String getComment() {
301        return comment;
302    }
303
304    /**
305     * Returns true if this is a directory entry. A directory entry is
306     * defined to be one whose name ends with a '/'.
307     * @return true if this is a directory entry
308     */
309    public boolean isDirectory() {
310        return name.endsWith("/");
311    }
312
313    /**
314     * Returns a string representation of the ZIP entry.
315     */
316    public String toString() {
317        return getName();
318    }
319
320    /*
321     * Converts DOS time to Java time (number of milliseconds since epoch).
322     */
323    private static long dosToJavaTime(long dtime) {
324        Date d = new Date((int)(((dtime >> 25) & 0x7f) + 80),
325                          (int)(((dtime >> 21) & 0x0f) - 1),
326                          (int)((dtime >> 16) & 0x1f),
327                          (int)((dtime >> 11) & 0x1f),
328                          (int)((dtime >> 5) & 0x3f),
329                          (int)((dtime << 1) & 0x3e));
330        return d.getTime();
331    }
332
333    /*
334     * Converts Java time to DOS time.
335     */
336    private static long javaToDosTime(long time) {
337        Date d = new Date(time);
338        int year = d.getYear() + 1900;
339        if (year < 1980) {
340            return (1 << 21) | (1 << 16);
341        }
342        return (year - 1980) << 25 | (d.getMonth() + 1) << 21 |
343               d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 |
344               d.getSeconds() >> 1;
345    }
346
347    /**
348     * Returns the hash code value for this entry.
349     */
350    public int hashCode() {
351        return name.hashCode();
352    }
353
354    /**
355     * Returns a copy of this entry.
356     */
357    public Object clone() {
358        try {
359            ZipEntry e = (ZipEntry)super.clone();
360            e.extra = (extra == null) ? null : extra.clone();
361            return e;
362        } catch (CloneNotSupportedException e) {
363            // This should never happen, since we are Cloneable
364            throw new InternalError();
365        }
366    }
367}
368