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