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 int KNOWN_SIZE = 1; 054 private static final int KNOWN_CSIZE = 2; 055 private static final int KNOWN_CRC = 4; 056 private static final int KNOWN_TIME = 8; 057 private static final int KNOWN_EXTRA = 16; 058 059 private static Calendar cal; 060 061 private String name; 062 private int size; 063 private long compressedSize = -1; 064 private int crc; 065 private int dostime; 066 private short known = 0; 067 private short method = -1; 068 private byte[] extra = null; 069 private String comment = null; 070 071 int flags; /* used by ZipOutputStream */ 072 int offset; /* used by ZipFile and ZipOutputStream */ 073 074 /** 075 * Compression method. This method doesn't compress at all. 076 */ 077 public static final int STORED = 0; 078 /** 079 * Compression method. This method uses the Deflater. 080 */ 081 public static final int DEFLATED = 8; 082 083 /** 084 * Creates a zip entry with the given name. 085 * @param name the name. May include directory components separated 086 * by '/'. 087 * 088 * @exception NullPointerException when name is null. 089 * @exception IllegalArgumentException when name is bigger then 65535 chars. 090 */ 091 public ZipEntry(String name) 092 { 093 int length = name.length(); 094 if (length > 65535) 095 throw new IllegalArgumentException("name length is " + length); 096 this.name = name; 097 } 098 099 /** 100 * Creates a copy of the given zip entry. 101 * @param e the entry to copy. 102 */ 103 public ZipEntry(ZipEntry e) 104 { 105 this(e, e.name); 106 } 107 108 ZipEntry(ZipEntry e, String name) 109 { 110 this.name = name; 111 known = e.known; 112 size = e.size; 113 compressedSize = e.compressedSize; 114 crc = e.crc; 115 dostime = e.dostime; 116 method = e.method; 117 extra = e.extra; 118 comment = e.comment; 119 } 120 121 final void setDOSTime(int dostime) 122 { 123 this.dostime = dostime; 124 known |= KNOWN_TIME; 125 } 126 127 final int getDOSTime() 128 { 129 if ((known & KNOWN_TIME) == 0) 130 return 0; 131 else 132 return dostime; 133 } 134 135 /** 136 * Creates a copy of this zip entry. 137 */ 138 /** 139 * Clones the entry. 140 */ 141 public Object clone() 142 { 143 try 144 { 145 // The JCL says that the `extra' field is also copied. 146 ZipEntry clone = (ZipEntry) super.clone(); 147 if (extra != null) 148 clone.extra = (byte[]) extra.clone(); 149 return clone; 150 } 151 catch (CloneNotSupportedException ex) 152 { 153 throw new InternalError(); 154 } 155 } 156 157 /** 158 * Returns the entry name. The path components in the entry are 159 * always separated by slashes ('/'). 160 */ 161 public String getName() 162 { 163 return name; 164 } 165 166 /** 167 * Sets the time of last modification of the entry. 168 * @time the time of last modification of the entry. 169 */ 170 public void setTime(long time) 171 { 172 Calendar cal = getCalendar(); 173 synchronized (cal) 174 { 175 cal.setTimeInMillis(time); 176 dostime = (cal.get(Calendar.YEAR) - 1980 & 0x7f) << 25 177 | (cal.get(Calendar.MONTH) + 1) << 21 178 | (cal.get(Calendar.DAY_OF_MONTH)) << 16 179 | (cal.get(Calendar.HOUR_OF_DAY)) << 11 180 | (cal.get(Calendar.MINUTE)) << 5 181 | (cal.get(Calendar.SECOND)) >> 1; 182 } 183 this.known |= KNOWN_TIME; 184 } 185 186 /** 187 * Gets the time of last modification of the entry. 188 * @return the time of last modification of the entry, or -1 if unknown. 189 */ 190 public long getTime() 191 { 192 // The extra bytes might contain the time (posix/unix extension) 193 parseExtra(); 194 195 if ((known & KNOWN_TIME) == 0) 196 return -1; 197 198 int sec = 2 * (dostime & 0x1f); 199 int min = (dostime >> 5) & 0x3f; 200 int hrs = (dostime >> 11) & 0x1f; 201 int day = (dostime >> 16) & 0x1f; 202 int mon = ((dostime >> 21) & 0xf) - 1; 203 int year = ((dostime >> 25) & 0x7f) + 1980; /* since 1900 */ 204 205 try 206 { 207 cal = getCalendar(); 208 synchronized (cal) 209 { 210 cal.set(year, mon, day, hrs, min, sec); 211 return cal.getTimeInMillis(); 212 } 213 } 214 catch (RuntimeException ex) 215 { 216 /* Ignore illegal time stamp */ 217 known &= ~KNOWN_TIME; 218 return -1; 219 } 220 } 221 222 private static synchronized Calendar getCalendar() 223 { 224 if (cal == null) 225 cal = Calendar.getInstance(); 226 227 return cal; 228 } 229 230 /** 231 * Sets the size of the uncompressed data. 232 * @exception IllegalArgumentException if size is not in 0..0xffffffffL 233 */ 234 public void setSize(long size) 235 { 236 if ((size & 0xffffffff00000000L) != 0) 237 throw new IllegalArgumentException(); 238 this.size = (int) size; 239 this.known |= KNOWN_SIZE; 240 } 241 242 /** 243 * Gets the size of the uncompressed data. 244 * @return the size or -1 if unknown. 245 */ 246 public long getSize() 247 { 248 return (known & KNOWN_SIZE) != 0 ? size & 0xffffffffL : -1L; 249 } 250 251 /** 252 * Sets the size of the compressed data. 253 */ 254 public void setCompressedSize(long csize) 255 { 256 this.compressedSize = csize; 257 } 258 259 /** 260 * Gets the size of the compressed data. 261 * @return the size or -1 if unknown. 262 */ 263 public long getCompressedSize() 264 { 265 return compressedSize; 266 } 267 268 /** 269 * Sets the crc of the uncompressed data. 270 * @exception IllegalArgumentException if crc is not in 0..0xffffffffL 271 */ 272 public void setCrc(long crc) 273 { 274 if ((crc & 0xffffffff00000000L) != 0) 275 throw new IllegalArgumentException(); 276 this.crc = (int) crc; 277 this.known |= KNOWN_CRC; 278 } 279 280 /** 281 * Gets the crc of the uncompressed data. 282 * @return the crc or -1 if unknown. 283 */ 284 public long getCrc() 285 { 286 return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L; 287 } 288 289 /** 290 * Sets the compression method. Only DEFLATED and STORED are 291 * supported. 292 * @exception IllegalArgumentException if method is not supported. 293 * @see ZipOutputStream#DEFLATED 294 * @see ZipOutputStream#STORED 295 */ 296 public void setMethod(int method) 297 { 298 if (method != ZipOutputStream.STORED 299 && method != ZipOutputStream.DEFLATED) 300 throw new IllegalArgumentException(); 301 this.method = (short) method; 302 } 303 304 /** 305 * Gets the compression method. 306 * @return the compression method or -1 if unknown. 307 */ 308 public int getMethod() 309 { 310 return method; 311 } 312 313 /** 314 * Sets the extra data. 315 * @exception IllegalArgumentException if extra is longer than 0xffff bytes. 316 */ 317 public void setExtra(byte[] extra) 318 { 319 if (extra == null) 320 { 321 this.extra = null; 322 return; 323 } 324 if (extra.length > 0xffff) 325 throw new IllegalArgumentException(); 326 this.extra = extra; 327 } 328 329 private void parseExtra() 330 { 331 // Already parsed? 332 if ((known & KNOWN_EXTRA) != 0) 333 return; 334 335 if (extra == null) 336 { 337 known |= KNOWN_EXTRA; 338 return; 339 } 340 341 try 342 { 343 int pos = 0; 344 while (pos < extra.length) 345 { 346 int sig = (extra[pos++] & 0xff) 347 | (extra[pos++] & 0xff) << 8; 348 int len = (extra[pos++] & 0xff) 349 | (extra[pos++] & 0xff) << 8; 350 if (sig == 0x5455) 351 { 352 /* extended time stamp */ 353 int flags = extra[pos]; 354 if ((flags & 1) != 0) 355 { 356 long time = ((extra[pos+1] & 0xff) 357 | (extra[pos+2] & 0xff) << 8 358 | (extra[pos+3] & 0xff) << 16 359 | (extra[pos+4] & 0xff) << 24); 360 setTime(time); 361 } 362 } 363 pos += len; 364 } 365 } 366 catch (ArrayIndexOutOfBoundsException ex) 367 { 368 /* be lenient */ 369 } 370 371 known |= KNOWN_EXTRA; 372 return; 373 } 374 375 /** 376 * Gets the extra data. 377 * @return the extra data or null if not set. 378 */ 379 public byte[] getExtra() 380 { 381 return extra; 382 } 383 384 /** 385 * Sets the entry comment. 386 * @exception IllegalArgumentException if comment is longer than 0xffff. 387 */ 388 public void setComment(String comment) 389 { 390 if (comment != null && comment.length() > 0xffff) 391 throw new IllegalArgumentException(); 392 this.comment = comment; 393 } 394 395 /** 396 * Gets the comment. 397 * @return the comment or null if not set. 398 */ 399 public String getComment() 400 { 401 return comment; 402 } 403 404 /** 405 * Gets true, if the entry is a directory. This is solely 406 * determined by the name, a trailing slash '/' marks a directory. 407 */ 408 public boolean isDirectory() 409 { 410 int nlen = name.length(); 411 return nlen > 0 && name.charAt(nlen - 1) == '/'; 412 } 413 414 /** 415 * Gets the string representation of this ZipEntry. This is just 416 * the name as returned by getName(). 417 */ 418 public String toString() 419 { 420 return name; 421 } 422 423 /** 424 * Gets the hashCode of this ZipEntry. This is just the hashCode 425 * of the name. Note that the equals method isn't changed, though. 426 */ 427 public int hashCode() 428 { 429 return name.hashCode(); 430 } 431 }