Source for java.util.zip.ZipEntry

   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:    */
  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:    */
 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: }