001 /* ZipEntry.java -- 002 Copyright (C) 2001, 2002, 2004, 2005 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath 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 for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 039 package java.util.zip; 040 041 import java.util.Calendar; 042 043 /** 044 * This class represents a member of a zip archive. ZipFile and 045 * ZipInputStream will give you instances of this class as information 046 * about the members in an archive. On the other hand ZipOutputStream 047 * needs an instance of this class to create a new member. 048 * 049 * @author Jochen Hoenicke 050 */ 051 public class ZipEntry implements ZipConstants, Cloneable 052 { 053 private static final byte KNOWN_SIZE = 1; 054 private static final byte KNOWN_CSIZE = 2; 055 private static final byte KNOWN_CRC = 4; 056 private static final byte KNOWN_TIME = 8; 057 private static final byte KNOWN_DOSTIME = 16; 058 private static final byte KNOWN_EXTRA = 32; 059 060 /** Immutable name of the entry */ 061 private final String name; 062 /** Uncompressed size */ 063 private int size; 064 /** Compressed size */ 065 private long compressedSize = -1; 066 /** CRC of uncompressed data */ 067 private int crc; 068 /** Comment or null if none */ 069 private String comment = null; 070 /** The compression method. Either DEFLATED or STORED, by default -1. */ 071 private byte method = -1; 072 /** Flags specifying what we know about this entry */ 073 private byte known = 0; 074 /** 075 * The 32bit DOS encoded format for the time of this entry. Only valid if 076 * KNOWN_DOSTIME is set in known. 077 */ 078 private int dostime; 079 /** 080 * The 64bit Java encoded millisecond time since the beginning of the epoch. 081 * Only valid if KNOWN_TIME is set in known. 082 */ 083 private long time; 084 /** Extra data */ 085 private byte[] extra = null; 086 087 int flags; /* used by ZipOutputStream */ 088 int offset; /* used by ZipFile and ZipOutputStream */ 089 090 /** 091 * Compression method. This method doesn't compress at all. 092 */ 093 public static final int STORED = 0; 094 /** 095 * Compression method. This method uses the Deflater. 096 */ 097 public static final int DEFLATED = 8; 098 099 /** 100 * Creates a zip entry with the given name. 101 * @param name the name. May include directory components separated 102 * by '/'. 103 * 104 * @exception NullPointerException when name is null. 105 * @exception IllegalArgumentException when name is bigger then 65535 chars. 106 */ 107 public ZipEntry(String name) 108 { 109 int length = name.length(); 110 if (length > 65535) 111 throw new IllegalArgumentException("name length is " + length); 112 this.name = name; 113 } 114 115 /** 116 * Creates a copy of the given zip entry. 117 * @param e the entry to copy. 118 */ 119 public ZipEntry(ZipEntry e) 120 { 121 this(e, e.name); 122 } 123 124 ZipEntry(ZipEntry e, String name) 125 { 126 this.name = name; 127 known = e.known; 128 size = e.size; 129 compressedSize = e.compressedSize; 130 crc = e.crc; 131 dostime = e.dostime; 132 time = e.time; 133 method = e.method; 134 extra = e.extra; 135 comment = e.comment; 136 } 137 138 final void setDOSTime(int dostime) 139 { 140 this.dostime = dostime; 141 known |= KNOWN_DOSTIME; 142 known &= ~KNOWN_TIME; 143 } 144 145 final int getDOSTime() 146 { 147 if ((known & KNOWN_DOSTIME) != 0) 148 return dostime; 149 else if ((known & KNOWN_TIME) != 0) 150 { 151 Calendar cal = Calendar.getInstance(); 152 cal.setTimeInMillis(time); 153 dostime = (cal.get(Calendar.YEAR) - 1980 & 0x7f) << 25 154 | (cal.get(Calendar.MONTH) + 1) << 21 155 | (cal.get(Calendar.DAY_OF_MONTH)) << 16 156 | (cal.get(Calendar.HOUR_OF_DAY)) << 11 157 | (cal.get(Calendar.MINUTE)) << 5 158 | (cal.get(Calendar.SECOND)) >> 1; 159 known |= KNOWN_DOSTIME; 160 return dostime; 161 } 162 else 163 return 0; 164 } 165 166 /** 167 * Creates a copy of this zip entry. 168 */ 169 public Object clone() 170 { 171 // JCL defines this as being the same as the copy constructor above, 172 // except that value of the "extra" field is also copied. Take care 173 // that in the case of a subclass we use clone() rather than the copy 174 // constructor. 175 ZipEntry clone; 176 if (this.getClass() == ZipEntry.class) 177 clone = new ZipEntry(this); 178 else 179 { 180 try 181 { 182 clone = (ZipEntry) super.clone(); 183 } 184 catch (CloneNotSupportedException e) 185 { 186 throw new InternalError(); 187 } 188 } 189 if (extra != null) 190 { 191 clone.extra = new byte[extra.length]; 192 System.arraycopy(extra, 0, clone.extra, 0, extra.length); 193 } 194 return clone; 195 } 196 197 /** 198 * Returns the entry name. The path components in the entry are 199 * always separated by slashes ('/'). 200 */ 201 public String getName() 202 { 203 return name; 204 } 205 206 /** 207 * Sets the time of last modification of the entry. 208 * @time the time of last modification of the entry. 209 */ 210 public void setTime(long time) 211 { 212 this.time = time; 213 this.known |= KNOWN_TIME; 214 this.known &= ~KNOWN_DOSTIME; 215 } 216 217 /** 218 * Gets the time of last modification of the entry. 219 * @return the time of last modification of the entry, or -1 if unknown. 220 */ 221 public long getTime() 222 { 223 // The extra bytes might contain the time (posix/unix extension) 224 parseExtra(); 225 226 if ((known & KNOWN_TIME) != 0) 227 return time; 228 else if ((known & KNOWN_DOSTIME) != 0) 229 { 230 int sec = 2 * (dostime & 0x1f); 231 int min = (dostime >> 5) & 0x3f; 232 int hrs = (dostime >> 11) & 0x1f; 233 int day = (dostime >> 16) & 0x1f; 234 int mon = ((dostime >> 21) & 0xf) - 1; 235 int year = ((dostime >> 25) & 0x7f) + 1980; /* since 1900 */ 236 237 try 238 { 239 Calendar cal = Calendar.getInstance(); 240 cal.set(year, mon, day, hrs, min, sec); 241 time = cal.getTimeInMillis(); 242 known |= KNOWN_TIME; 243 return time; 244 } 245 catch (RuntimeException ex) 246 { 247 /* Ignore illegal time stamp */ 248 known &= ~KNOWN_TIME; 249 return -1; 250 } 251 } 252 else 253 return -1; 254 } 255 256 /** 257 * Sets the size of the uncompressed data. 258 * @exception IllegalArgumentException if size is not in 0..0xffffffffL 259 */ 260 public void setSize(long size) 261 { 262 if ((size & 0xffffffff00000000L) != 0) 263 throw new IllegalArgumentException(); 264 this.size = (int) size; 265 this.known |= KNOWN_SIZE; 266 } 267 268 /** 269 * Gets the size of the uncompressed data. 270 * @return the size or -1 if unknown. 271 */ 272 public long getSize() 273 { 274 return (known & KNOWN_SIZE) != 0 ? size & 0xffffffffL : -1L; 275 } 276 277 /** 278 * Sets the size of the compressed data. 279 */ 280 public void setCompressedSize(long csize) 281 { 282 this.compressedSize = csize; 283 } 284 285 /** 286 * Gets the size of the compressed data. 287 * @return the size or -1 if unknown. 288 */ 289 public long getCompressedSize() 290 { 291 return compressedSize; 292 } 293 294 /** 295 * Sets the crc of the uncompressed data. 296 * @exception IllegalArgumentException if crc is not in 0..0xffffffffL 297 */ 298 public void setCrc(long crc) 299 { 300 if ((crc & 0xffffffff00000000L) != 0) 301 throw new IllegalArgumentException(); 302 this.crc = (int) crc; 303 this.known |= KNOWN_CRC; 304 } 305 306 /** 307 * Gets the crc of the uncompressed data. 308 * @return the crc or -1 if unknown. 309 */ 310 public long getCrc() 311 { 312 return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L; 313 } 314 315 /** 316 * Sets the compression method. Only DEFLATED and STORED are 317 * supported. 318 * @exception IllegalArgumentException if method is not supported. 319 * @see ZipOutputStream#DEFLATED 320 * @see ZipOutputStream#STORED 321 */ 322 public void setMethod(int method) 323 { 324 if (method != ZipOutputStream.STORED 325 && method != ZipOutputStream.DEFLATED) 326 throw new IllegalArgumentException(); 327 this.method = (byte) method; 328 } 329 330 /** 331 * Gets the compression method. 332 * @return the compression method or -1 if unknown. 333 */ 334 public int getMethod() 335 { 336 return method; 337 } 338 339 /** 340 * Sets the extra data. 341 * @exception IllegalArgumentException if extra is longer than 0xffff bytes. 342 */ 343 public void setExtra(byte[] extra) 344 { 345 if (extra == null) 346 { 347 this.extra = null; 348 return; 349 } 350 if (extra.length > 0xffff) 351 throw new IllegalArgumentException(); 352 this.extra = extra; 353 } 354 355 private void parseExtra() 356 { 357 // Already parsed? 358 if ((known & KNOWN_EXTRA) != 0) 359 return; 360 361 if (extra == null) 362 { 363 known |= KNOWN_EXTRA; 364 return; 365 } 366 367 try 368 { 369 int pos = 0; 370 while (pos < extra.length) 371 { 372 int sig = (extra[pos++] & 0xff) 373 | (extra[pos++] & 0xff) << 8; 374 int len = (extra[pos++] & 0xff) 375 | (extra[pos++] & 0xff) << 8; 376 if (sig == 0x5455) 377 { 378 /* extended time stamp */ 379 int flags = extra[pos]; 380 if ((flags & 1) != 0) 381 { 382 long time = ((extra[pos+1] & 0xff) 383 | (extra[pos+2] & 0xff) << 8 384 | (extra[pos+3] & 0xff) << 16 385 | (extra[pos+4] & 0xff) << 24); 386 setTime(time*1000); 387 } 388 } 389 pos += len; 390 } 391 } 392 catch (ArrayIndexOutOfBoundsException ex) 393 { 394 /* be lenient */ 395 } 396 397 known |= KNOWN_EXTRA; 398 return; 399 } 400 401 /** 402 * Gets the extra data. 403 * @return the extra data or null if not set. 404 */ 405 public byte[] getExtra() 406 { 407 return extra; 408 } 409 410 /** 411 * Sets the entry comment. 412 * @exception IllegalArgumentException if comment is longer than 0xffff. 413 */ 414 public void setComment(String comment) 415 { 416 if (comment != null && comment.length() > 0xffff) 417 throw new IllegalArgumentException(); 418 this.comment = comment; 419 } 420 421 /** 422 * Gets the comment. 423 * @return the comment or null if not set. 424 */ 425 public String getComment() 426 { 427 return comment; 428 } 429 430 /** 431 * Gets true, if the entry is a directory. This is solely 432 * determined by the name, a trailing slash '/' marks a directory. 433 */ 434 public boolean isDirectory() 435 { 436 int nlen = name.length(); 437 return nlen > 0 && name.charAt(nlen - 1) == '/'; 438 } 439 440 /** 441 * Gets the string representation of this ZipEntry. This is just 442 * the name as returned by getName(). 443 */ 444 public String toString() 445 { 446 return name; 447 } 448 449 /** 450 * Gets the hashCode of this ZipEntry. This is just the hashCode 451 * of the name. Note that the equals method isn't changed, though. 452 */ 453 public int hashCode() 454 { 455 return name.hashCode(); 456 } 457 }