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