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 int KNOWN_SIZE = 1; 54 private static final int KNOWN_CSIZE = 2; 55 private static final int KNOWN_CRC = 4; 56 private static final int KNOWN_TIME = 8; 57 private static final int KNOWN_EXTRA = 16; 58 59 private static Calendar cal; 60 61 private String name; 62 private int size; 63 private long compressedSize = -1; 64 private int crc; 65 private int dostime; 66 private short known = 0; 67 private short method = -1; 68 private byte[] extra = null; 69 private String comment = null; 70 71 int flags; /* used by ZipOutputStream */ 72 int offset; /* used by ZipFile and ZipOutputStream */ 73 74 /** 75 * Compression method. This method doesn't compress at all. 76 */ 77 public static final int STORED = 0; 78 /** 79 * Compression method. This method uses the Deflater. 80 */ 81 public static final int DEFLATED = 8; 82 83 /** 84 * Creates a zip entry with the given name. 85 * @param name the name. May include directory components separated 86 * by '/'. 87 * 88 * @exception NullPointerException when name is null. 89 * @exception IllegalArgumentException when name is bigger then 65535 chars. 90 */ ZipEntry(String name)91 public ZipEntry(String name) 92 { 93 int length = name.length(); 94 if (length > 65535) 95 throw new IllegalArgumentException("name length is " + length); 96 this.name = name; 97 } 98 99 /** 100 * Creates a copy of the given zip entry. 101 * @param e the entry to copy. 102 */ ZipEntry(ZipEntry e)103 public ZipEntry(ZipEntry e) 104 { 105 this(e, e.name); 106 } 107 ZipEntry(ZipEntry e, String name)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 setDOSTime(int dostime)121 final void setDOSTime(int dostime) 122 { 123 this.dostime = dostime; 124 known |= KNOWN_TIME; 125 } 126 getDOSTime()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 */ clone()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 */ getName()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 */ setTime(long time)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 */ getTime()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 getCalendar()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 */ setSize(long size)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 */ getSize()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 */ setCompressedSize(long csize)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 */ getCompressedSize()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 */ setCrc(long crc)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 */ getCrc()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 */ setMethod(int method)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 */ getMethod()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 */ setExtra(byte[] extra)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 parseExtra()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 */ getExtra()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 */ setComment(String comment)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 */ getComment()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 */ isDirectory()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 */ toString()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 */ hashCode()427 public int hashCode() 428 { 429 return name.hashCode(); 430 } 431 } 432