001: /*
002: * @(#)ZipEntry.java 1.41 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.util.zip;
029:
030: import java.util.Date;
031: import java.util.Calendar;
032:
033: /**
034: * This class is used to represent a ZIP file entry.
035: *
036: * @version 1.32, 05/03/00
037: * @author David Connelly
038: */
039: public class ZipEntry implements ZipConstants, Cloneable {
040: String name; // entry name
041: long time = -1; // modification time (in DOS time)
042: long crc = -1; // crc-32 of entry data
043: long size = -1; // uncompressed size of entry data
044: long csize = -1; // compressed size of entry data
045: int method = -1; // compression method
046: byte[] extra; // optional extra field data for entry
047: String comment; // optional comment string for entry
048: // The following flags are used only by Zip{Input,Output}Stream
049: int flag; // bit flags
050: int version; // version needed to extract
051: long offset; // offset of loc header
052:
053: /**
054: * Compression method for uncompressed entries.
055: */
056: public static final int STORED = 0;
057:
058: /**
059: * Compression method for compressed (deflated) entries.
060: */
061: public static final int DEFLATED = 8;
062:
063: static {
064: /* load the zip library */
065: java.security.AccessController
066: .doPrivileged(new sun.security.action.LoadLibraryAction(
067: "zip"));
068: initIDs();
069: }
070:
071: private static native void initIDs();
072:
073: /**
074: * Creates a new zip entry with the specified name.
075: *
076: * @param name the entry name
077: * @exception NullPointerException if the entry name is null
078: * @exception IllegalArgumentException if the entry name is longer than
079: * 0xFFFF bytes
080: */
081: public ZipEntry(String name) {
082: if (name == null) {
083: throw new NullPointerException();
084: }
085: if (name.length() > 0xFFFF) {
086: throw new IllegalArgumentException("entry name too long");
087: }
088: this .name = name;
089: }
090:
091: /**
092: * Creates a new zip entry with fields taken from the specified
093: * zip entry.
094: * @param e a zip Entry object
095: */
096: public ZipEntry(ZipEntry e) {
097: name = e.name;
098: time = e.time;
099: crc = e.crc;
100: size = e.size;
101: csize = e.csize;
102: method = e.method;
103: extra = e.extra;
104: comment = e.comment;
105: }
106:
107: /*
108: * Creates a new zip entry for the given name with fields initialized
109: * from the specified jzentry data.
110: */
111: ZipEntry(String name, long jzentry) {
112: this .name = name;
113: initFields(jzentry);
114: }
115:
116: private native void initFields(long jzentry);
117:
118: /*
119: * Creates a new zip entry with fields initialized from the specified
120: * jzentry data.
121: */
122: ZipEntry(long jzentry) {
123: initFields(jzentry);
124: }
125:
126: /**
127: * Returns the name of the entry.
128: * @return the name of the entry
129: */
130: public String getName() {
131: return name;
132: }
133:
134: /**
135: * Sets the modification time of the entry.
136: * @param time the entry modification time in number of milliseconds
137: * since the epoch
138: * @see #getTime()
139: */
140: public void setTime(long time) {
141: this .time = javaToDosTime(time);
142: }
143:
144: /**
145: * Returns the modification time of the entry, or -1 if not specified.
146: * @return the modification time of the entry, or -1 if not specified
147: * @see #setTime(long)
148: */
149: public long getTime() {
150: return time != -1 ? dosToJavaTime(time) : -1;
151: }
152:
153: /**
154: * Sets the uncompressed size of the entry data.
155: * @param size the uncompressed size in bytes
156: * @exception IllegalArgumentException if the specified size is less
157: * than 0 or greater than 0xFFFFFFFF bytes
158: * @see #getSize()
159: */
160: public void setSize(long size) {
161: if (size < 0 || size > 0xFFFFFFFFL) {
162: throw new IllegalArgumentException("invalid entry size");
163: }
164: this .size = size;
165: }
166:
167: /**
168: * Returns the uncompressed size of the entry data, or -1 if not known.
169: * @return the uncompressed size of the entry data, or -1 if not known
170: * @see #setSize(long)
171: */
172: public long getSize() {
173: return size;
174: }
175:
176: /**
177: * Returns the size of the compressed entry data, or -1 if not known.
178: * In the case of a stored entry, the compressed size will be the same
179: * as the uncompressed size of the entry.
180: * @return the size of the compressed entry data, or -1 if not known
181: * @see #setCompressedSize(long)
182: */
183: public long getCompressedSize() {
184: return csize;
185: }
186:
187: /**
188: * Sets the size of the compressed entry data.
189: * @param csize the compressed size to set to
190: * @see #getCompressedSize()
191: */
192: public void setCompressedSize(long csize) {
193: this .csize = csize;
194: }
195:
196: /**
197: * Sets the CRC-32 checksum of the uncompressed entry data.
198: * @param crc the CRC-32 value
199: * @exception IllegalArgumentException if the specified CRC-32 value is
200: * less than 0 or greater than 0xFFFFFFFF
201: * @see #setCrc(long)
202: */
203: public void setCrc(long crc) {
204: if (crc < 0 || crc > 0xFFFFFFFFL) {
205: throw new IllegalArgumentException("invalid entry crc-32");
206: }
207: this .crc = crc;
208: }
209:
210: /**
211: * Returns the CRC-32 checksum of the uncompressed entry data, or -1 if
212: * not known.
213: * @return the CRC-32 checksum of the uncompressed entry data, or -1 if
214: * not known
215: * @see #getCrc()
216: */
217: public long getCrc() {
218: return crc;
219: }
220:
221: /**
222: * Sets the compression method for the entry.
223: * @param method the compression method, either STORED or DEFLATED
224: * @exception IllegalArgumentException if the specified compression
225: * method is invalid
226: * @see #getMethod()
227: */
228: public void setMethod(int method) {
229: if (method != STORED && method != DEFLATED) {
230: throw new IllegalArgumentException(
231: "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(
255: "invalid extra field length");
256: }
257: this .extra = extra;
258: }
259:
260: /**
261: * Returns the extra field data for the entry, or null if none.
262: * @return the extra field data for the entry, or null if none
263: * @see #setExtra(byte[])
264: */
265: public byte[] getExtra() {
266: return extra;
267: }
268:
269: /**
270: * Sets the optional comment string for the entry.
271: * @param comment the comment string
272: * @exception IllegalArgumentException if the length of the specified
273: * comment string is greater than 0xFFFF bytes
274: * @see #getComment()
275: */
276: public void setComment(String comment) {
277: if (comment != null && comment.length() > 0xffff / 3
278: && getUTF8Length(comment) > 0xffff) {
279: throw new IllegalArgumentException(
280: "invalid entry comment length");
281: }
282: this .comment = comment;
283: }
284:
285: /**
286: * Returns the comment string for the entry, or null if none.
287: * @return the comment string for the entry, or null if none
288: * @see #setComment(String)
289: */
290: public String getComment() {
291: return comment;
292: }
293:
294: /**
295: * Returns true if this is a directory entry. A directory entry is
296: * defined to be one whose name ends with a '/'.
297: * @return true if this is a directory entry
298: */
299: public boolean isDirectory() {
300: return name.endsWith("/");
301: }
302:
303: /**
304: * Returns a string representation of the ZIP entry.
305: */
306: public String toString() {
307: return getName();
308: }
309:
310: /*
311: * Converts DOS time to Java time (number of milliseconds since epoch).
312: */
313: private static long dosToJavaTime(long dtime) {
314: Calendar c = Calendar.getInstance();
315: c.clear();
316: c.set((int) (((dtime >> 25) & 0x7f) + 80 + 1900),
317: (int) (((dtime >> 21) & 0x0f) - 1),
318: (int) ((dtime >> 16) & 0x1f),
319: (int) ((dtime >> 11) & 0x1f),
320: (int) ((dtime >> 5) & 0x3f),
321: (int) ((dtime << 1) & 0x3e));
322: return c.getTime().getTime();
323: }
324:
325: /*
326: * Converts Java time to DOS time.
327: */
328: private static long javaToDosTime(long time) {
329: Calendar C = Calendar.getInstance();
330: C.setTime(new Date(time));
331: int year = C.get(Calendar.YEAR);
332: if (year < 1980) {
333: return (1 << 21) | (1 << 16);
334: }
335:
336: return (year - 1980) << 25 | (C.get(Calendar.MONTH) + 1) << 21
337: | C.get(Calendar.DAY_OF_MONTH) << 16
338: | C.get(Calendar.HOUR_OF_DAY) << 11
339: | C.get(Calendar.MINUTE) << 5
340: | C.get(Calendar.SECOND) >> 1;
341: }
342:
343: /**
344: * Returns the hash code value for this entry.
345: */
346: public int hashCode() {
347: return name.hashCode();
348: }
349:
350: /**
351: * Returns a copy of this entry.
352: */
353: public Object clone() {
354: try {
355: ZipEntry e = (ZipEntry) super .clone();
356: e.extra = (extra == null ? null : (byte[]) extra.clone());
357: return e;
358: } catch (CloneNotSupportedException e) {
359: // This should never happen, since we are Cloneable
360: throw new InternalError();
361: }
362: }
363:
364: /*
365: * Returns the length of String's UTF8 encoding.
366: */
367: static int getUTF8Length(String s) {
368: int count = 0;
369: for (int i = 0; i < s.length(); i++) {
370: char ch = s.charAt(i);
371: if (ch <= 0x7f) {
372: count++;
373: } else if (ch <= 0x7ff) {
374: count += 2;
375: } else {
376: count += 3;
377: }
378: }
379: return count;
380: }
381: }
|