1 /* ZipEntry.java -- 2 Copyright (C) 2001, 2002, 2004, 2005 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 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. 10 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. 15 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., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 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. 25 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. */ 37 38 39 package java.util.zip; 40 41 import java.util.Calendar; 42 43 /** 44 * This class represents a member of a zip archive. ZipFile and 45 * ZipInputStream will give you instances of this class as information 46 * about the members in an archive. On the other hand ZipOutputStream 47 * needs an instance of this class to create a new member. 48 * 49 * @author Jochen Hoenicke 50 */ 51 public class ZipEntry implements ZipConstants, Cloneable 52 { 53 private static final byte KNOWN_SIZE = 1; 54 private static final byte KNOWN_CSIZE = 2; 55 private static final byte KNOWN_CRC = 4; 56 private static final byte KNOWN_TIME = 8; 57 private static final byte KNOWN_DOSTIME = 16; 58 private static final byte KNOWN_EXTRA = 32; 59 60 /** Immutable name of the entry */ 61 private final String name; 62 /** Uncompressed size */ 63 private int size; 64 /** Compressed size */ 65 private long compressedSize = -1; 66 /** CRC of uncompressed data */ 67 private int crc; 68 /** Comment or null if none */ 69 private String comment = null; 70 /** The compression method. Either DEFLATED or STORED, by default -1. */ 71 private byte method = -1; 72 /** Flags specifying what we know about this entry */ 73 private byte known = 0; 74 /** 75 * The 32bit DOS encoded format for the time of this entry. Only valid if 76 * KNOWN_DOSTIME is set in known. 77 */ 78 private int dostime; 79 /** 80 * The 64bit Java encoded millisecond time since the beginning of the epoch. 81 * Only valid if KNOWN_TIME is set in known. 82 */ 83 private long time; 84 /** Extra data */ 85 private byte[] extra = null; 86 87 int flags; /* used by ZipOutputStream */ 88 int offset; /* used by ZipFile and ZipOutputStream */ 89 90 /** 91 * Compression method. This method doesn't compress at all. 92 */ 93 public static final int STORED = 0; 94 /** 95 * Compression method. This method uses the Deflater. 96 */ 97 public static final int DEFLATED = 8; 98 99 /** 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 */ ZipEntry(String name)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 */ ZipEntry(ZipEntry e)119 public ZipEntry(ZipEntry e) 120 { 121 this(e, e.name); 122 } 123 ZipEntry(ZipEntry e, String name)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 setDOSTime(int dostime)138 final void setDOSTime(int dostime) 139 { 140 this.dostime = dostime; 141 known |= KNOWN_DOSTIME; 142 known &= ~KNOWN_TIME; 143 } 144 getDOSTime()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 */ clone()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 */ getName()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 */ setTime(long time)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 */ getTime()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 */ setSize(long size)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 */ getSize()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 */ setCompressedSize(long csize)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 */ getCompressedSize()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 */ setCrc(long crc)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 */ getCrc()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 */ setMethod(int method)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 */ getMethod()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 */ setExtra(byte[] extra)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 parseExtra()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 */ getExtra()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 */ setComment(String comment)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 */ getComment()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 */ isDirectory()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 */ toString()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 */ hashCode()453 public int hashCode() 454 { 455 return name.hashCode(); 456 } 457 } 458