ZipEntry.java 11.8 KB
Newer Older
Tom Tromey committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
/* ZipEntry.java --
   Copyright (C) 2001, 2002, 2004, 2005 Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package java.util.zip;

import java.util.Calendar;

/**
 * This class represents a member of a zip archive.  ZipFile and
 * ZipInputStream will give you instances of this class as information
 * about the members in an archive.  On the other hand ZipOutputStream
 * needs an instance of this class to create a new member.
 *
49
 * @author Jochen Hoenicke
Tom Tromey committed
50 51 52
 */
public class ZipEntry implements ZipConstants, Cloneable
{
53 54 55 56 57 58 59 60 61 62
  private static final byte KNOWN_SIZE    = 1;
  private static final byte KNOWN_CSIZE   = 2;
  private static final byte KNOWN_CRC     = 4;
  private static final byte KNOWN_TIME    = 8;
  private static final byte KNOWN_DOSTIME = 16;
  private static final byte KNOWN_EXTRA   = 32;

  /** Immutable name of the entry */
  private final String name;
  /** Uncompressed size */
Tom Tromey committed
63
  private int size;
64
  /** Compressed size */
Tom Tromey committed
65
  private long compressedSize = -1;
66
  /** CRC of uncompressed data */
Tom Tromey committed
67
  private int crc;
68 69 70 71 72 73 74 75 76 77
  /** Comment or null if none */
  private String comment = null;
  /** The compression method. Either DEFLATED or STORED, by default -1. */
  private byte method = -1;
  /** Flags specifying what we know about this entry */
  private byte known = 0;
  /**
   * The 32bit DOS encoded format for the time of this entry. Only valid if
   * KNOWN_DOSTIME is set in known.
   */
Tom Tromey committed
78
  private int dostime;
79 80 81 82 83 84
  /**
   * The 64bit Java encoded millisecond time since the beginning of the epoch.
   * Only valid if KNOWN_TIME is set in known.
   */
  private long time;
  /** Extra data */
Tom Tromey committed
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
  private byte[] extra = null;

  int flags;              /* used by ZipOutputStream */
  int offset;             /* used by ZipFile and ZipOutputStream */

  /**
   * Compression method.  This method doesn't compress at all.
   */
  public static final int STORED = 0;
  /**
   * Compression method.  This method uses the Deflater.
   */
  public static final int DEFLATED = 8;

  /**
   * Creates a zip entry with the given name.
   * @param name the name. May include directory components separated
   * by '/'.
   *
   * @exception NullPointerException when name is null.
   * @exception IllegalArgumentException when name is bigger then 65535 chars.
   */
  public ZipEntry(String name)
  {
    int length = name.length();
    if (length > 65535)
      throw new IllegalArgumentException("name length is " + length);
    this.name = name;
  }

  /**
   * Creates a copy of the given zip entry.
   * @param e the entry to copy.
   */
  public ZipEntry(ZipEntry e)
  {
    this(e, e.name);
  }

  ZipEntry(ZipEntry e, String name)
  {
    this.name = name;
    known = e.known;
    size = e.size;
    compressedSize = e.compressedSize;
    crc = e.crc;
    dostime = e.dostime;
132
    time = e.time;
Tom Tromey committed
133 134 135 136 137 138 139 140
    method = e.method;
    extra = e.extra;
    comment = e.comment;
  }

  final void setDOSTime(int dostime)
  {
    this.dostime = dostime;
141 142
    known |= KNOWN_DOSTIME;
    known &= ~KNOWN_TIME;
Tom Tromey committed
143 144 145 146
  }

  final int getDOSTime()
  {
147
    if ((known & KNOWN_DOSTIME) != 0)
Tom Tromey committed
148
      return dostime;
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
    else  if ((known & KNOWN_TIME) != 0)
      {
       Calendar cal = Calendar.getInstance();
       cal.setTimeInMillis(time);
       dostime = (cal.get(Calendar.YEAR) - 1980 & 0x7f) << 25
          | (cal.get(Calendar.MONTH) + 1) << 21
          | (cal.get(Calendar.DAY_OF_MONTH)) << 16
          | (cal.get(Calendar.HOUR_OF_DAY)) << 11
          | (cal.get(Calendar.MINUTE)) << 5
          | (cal.get(Calendar.SECOND)) >> 1;
       known |= KNOWN_DOSTIME;
       return dostime;
      }
    else
      return 0;
Tom Tromey committed
164 165 166 167 168 169 170
  }

  /**
   * Creates a copy of this zip entry.
   */
  public Object clone()
  {
171 172 173 174 175 176 177 178
    // JCL defines this as being the same as the copy constructor above,
    // except that value of the "extra" field is also copied. Take care
    // that in the case of a subclass we use clone() rather than the copy
    // constructor.
    ZipEntry clone;
    if (this.getClass() == ZipEntry.class)
      clone = new ZipEntry(this);
    else
Tom Tromey committed
179
      {
180 181 182 183 184 185 186 187
       try
         {
          clone = (ZipEntry) super.clone();
         }
       catch (CloneNotSupportedException e)
         {
          throw new InternalError();
         }
Tom Tromey committed
188
      }
189
    if (extra != null)
Tom Tromey committed
190
      {
191 192
       clone.extra = new byte[extra.length];
       System.arraycopy(extra, 0, clone.extra, 0, extra.length);
Tom Tromey committed
193
      }
194
    return clone;
Tom Tromey committed
195 196 197 198
  }

  /**
   * Returns the entry name.  The path components in the entry are
199
   * always separated by slashes ('/').
Tom Tromey committed
200 201 202 203 204 205 206 207 208 209 210 211
   */
  public String getName()
  {
    return name;
  }

  /**
   * Sets the time of last modification of the entry.
   * @time the time of last modification of the entry.
   */
  public void setTime(long time)
  {
212
    this.time = time;
Tom Tromey committed
213
    this.known |= KNOWN_TIME;
214
    this.known &= ~KNOWN_DOSTIME;
Tom Tromey committed
215 216 217 218 219 220 221 222
  }

  /**
   * Gets the time of last modification of the entry.
   * @return the time of last modification of the entry, or -1 if unknown.
   */
  public long getTime()
  {
Tom Tromey committed
223 224 225
    // The extra bytes might contain the time (posix/unix extension)
    parseExtra();

226 227 228
    if ((known & KNOWN_TIME) != 0)
      return time;
    else if ((known & KNOWN_DOSTIME) != 0)
Tom Tromey committed
229
      {
230 231 232 233 234 235 236 237 238 239 240 241 242 243
       int sec = 2 * (dostime & 0x1f);
       int min = (dostime >> 5) & 0x3f;
       int hrs = (dostime >> 11) & 0x1f;
       int day = (dostime >> 16) & 0x1f;
       int mon = ((dostime >> 21) & 0xf) - 1;
       int year = ((dostime >> 25) & 0x7f) + 1980; /* since 1900 */

       try
         {
          Calendar cal = Calendar.getInstance();
          cal.set(year, mon, day, hrs, min, sec);
          time = cal.getTimeInMillis();
          known |= KNOWN_TIME;
          return time;
244
         }
245 246 247 248 249 250
       catch (RuntimeException ex)
         {
          /* Ignore illegal time stamp */
          known &= ~KNOWN_TIME;
          return -1;
         }
Tom Tromey committed
251
      }
252 253
    else
      return -1;
Tom Tromey committed
254 255 256 257 258 259 260 261 262
  }

  /**
   * Sets the size of the uncompressed data.
   * @exception IllegalArgumentException if size is not in 0..0xffffffffL
   */
  public void setSize(long size)
  {
    if ((size & 0xffffffff00000000L) != 0)
263
        throw new IllegalArgumentException();
Tom Tromey committed
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
    this.size = (int) size;
    this.known |= KNOWN_SIZE;
  }

  /**
   * Gets the size of the uncompressed data.
   * @return the size or -1 if unknown.
   */
  public long getSize()
  {
    return (known & KNOWN_SIZE) != 0 ? size & 0xffffffffL : -1L;
  }

  /**
   * Sets the size of the compressed data.
   */
  public void setCompressedSize(long csize)
  {
    this.compressedSize = csize;
  }

  /**
   * Gets the size of the compressed data.
   * @return the size or -1 if unknown.
   */
  public long getCompressedSize()
  {
    return compressedSize;
  }

  /**
   * Sets the crc of the uncompressed data.
   * @exception IllegalArgumentException if crc is not in 0..0xffffffffL
   */
  public void setCrc(long crc)
  {
    if ((crc & 0xffffffff00000000L) != 0)
301
        throw new IllegalArgumentException();
Tom Tromey committed
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
    this.crc = (int) crc;
    this.known |= KNOWN_CRC;
  }

  /**
   * Gets the crc of the uncompressed data.
   * @return the crc or -1 if unknown.
   */
  public long getCrc()
  {
    return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L;
  }

  /**
   * Sets the compression method.  Only DEFLATED and STORED are
   * supported.
   * @exception IllegalArgumentException if method is not supported.
   * @see ZipOutputStream#DEFLATED
320
   * @see ZipOutputStream#STORED
Tom Tromey committed
321 322 323 324
   */
  public void setMethod(int method)
  {
    if (method != ZipOutputStream.STORED
325 326
        && method != ZipOutputStream.DEFLATED)
        throw new IllegalArgumentException();
327
    this.method = (byte) method;
Tom Tromey committed
328 329 330
  }

  /**
331
   * Gets the compression method.
Tom Tromey committed
332 333 334 335 336 337 338 339 340 341 342 343 344
   * @return the compression method or -1 if unknown.
   */
  public int getMethod()
  {
    return method;
  }

  /**
   * Sets the extra data.
   * @exception IllegalArgumentException if extra is longer than 0xffff bytes.
   */
  public void setExtra(byte[] extra)
  {
345
    if (extra == null)
Tom Tromey committed
346
      {
347 348
        this.extra = null;
        return;
Tom Tromey committed
349 350 351 352 353 354 355 356 357 358 359 360 361 362
      }
    if (extra.length > 0xffff)
      throw new IllegalArgumentException();
    this.extra = extra;
  }

  private void parseExtra()
  {
    // Already parsed?
    if ((known & KNOWN_EXTRA) != 0)
      return;

    if (extra == null)
      {
363 364
        known |= KNOWN_EXTRA;
        return;
Tom Tromey committed
365 366 367 368
      }

    try
      {
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
        int pos = 0;
        while (pos < extra.length)
          {
            int sig = (extra[pos++] & 0xff)
              | (extra[pos++] & 0xff) << 8;
            int len = (extra[pos++] & 0xff)
              | (extra[pos++] & 0xff) << 8;
            if (sig == 0x5455)
              {
                /* extended time stamp */
                int flags = extra[pos];
                if ((flags & 1) != 0)
                  {
                    long time = ((extra[pos+1] & 0xff)
                            | (extra[pos+2] & 0xff) << 8
                            | (extra[pos+3] & 0xff) << 16
                            | (extra[pos+4] & 0xff) << 24);
                    setTime(time*1000);
                  }
              }
            pos += len;
          }
Tom Tromey committed
391 392 393
      }
    catch (ArrayIndexOutOfBoundsException ex)
      {
394
        /* be lenient */
Tom Tromey committed
395 396 397
      }

    known |= KNOWN_EXTRA;
Tom Tromey committed
398
    return;
Tom Tromey committed
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
  }

  /**
   * Gets the extra data.
   * @return the extra data or null if not set.
   */
  public byte[] getExtra()
  {
    return extra;
  }

  /**
   * Sets the entry comment.
   * @exception IllegalArgumentException if comment is longer than 0xffff.
   */
  public void setComment(String comment)
  {
    if (comment != null && comment.length() > 0xffff)
      throw new IllegalArgumentException();
    this.comment = comment;
  }

  /**
   * Gets the comment.
   * @return the comment or null if not set.
   */
  public String getComment()
  {
    return comment;
  }

  /**
   * Gets true, if the entry is a directory.  This is solely
432
   * determined by the name, a trailing slash '/' marks a directory.
Tom Tromey committed
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
   */
  public boolean isDirectory()
  {
    int nlen = name.length();
    return nlen > 0 && name.charAt(nlen - 1) == '/';
  }

  /**
   * Gets the string representation of this ZipEntry.  This is just
   * the name as returned by getName().
   */
  public String toString()
  {
    return name;
  }

  /**
   * Gets the hashCode of this ZipEntry.  This is just the hashCode
   * of the name.  Note that the equals method isn't changed, though.
   */
  public int hashCode()
  {
    return name.hashCode();
  }
}