src/share/classes/java/util/zip/ZipEntry.java

Print this page


   1 /*
   2  * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.util.zip;
  27 





  28 /**
  29  * This class is used to represent a ZIP file entry.
  30  *
  31  * @author      David Connelly
  32  */
  33 public
  34 class ZipEntry implements ZipConstants, Cloneable {

  35     String name;        // entry name
  36     long mtime = -1;    // last modification time



  37     long crc = -1;      // crc-32 of entry data
  38     long size = -1;     // uncompressed size of entry data
  39     long csize = -1;    // compressed size of entry data
  40     int method = -1;    // compression method
  41     int flag = 0;       // general purpose flag
  42     byte[] extra;       // optional extra field data for entry
  43     String comment;     // optional comment string for entry
  44 
  45     /**
  46      * Compression method for uncompressed entries.
  47      */
  48     public static final int STORED = 0;
  49 
  50     /**
  51      * Compression method for compressed (deflated) entries.
  52      */
  53     public static final int DEFLATED = 8;
  54 
  55     /**
  56      * Creates a new zip entry with the specified name.
  57      *
  58      * @param name the entry name
  59      * @exception NullPointerException if the entry name is null
  60      * @exception IllegalArgumentException if the entry name is longer than


  61      *            0xFFFF bytes
  62      */
  63     public ZipEntry(String name) {
  64         if (name == null) {
  65             throw new NullPointerException();
  66         }
  67         if (name.length() > 0xFFFF) {
  68             throw new IllegalArgumentException("entry name too long");
  69         }
  70         this.name = name;
  71     }
  72 
  73     /**
  74      * Creates a new zip entry with fields taken from the specified
  75      * zip entry.
  76      * @param e a zip Entry object




  77      */
  78     public ZipEntry(ZipEntry e) {

  79         name = e.name;

  80         mtime = e.mtime;


  81         crc = e.crc;
  82         size = e.size;
  83         csize = e.csize;
  84         method = e.method;
  85         flag = e.flag;
  86         extra = e.extra;
  87         comment = e.comment;
  88     }
  89 
  90     /**
  91      * Creates a new un-initialized zip entry
  92      */
  93     ZipEntry() {}
  94 
  95     /**
  96      * Returns the name of the entry.
  97      * @return the name of the entry
  98      */
  99     public String getName() {
 100         return name;
 101     }
 102 
 103     /**
 104      * Sets the last modification time of the entry.
 105      *
 106      * @param time the last modification time of the entry in milliseconds since the epoch










 107      * @see #getTime()

 108      */
 109     public void setTime(long time) {
 110         this.mtime = time;

 111     }
 112 
 113     /**
 114      * Returns the last modification time of the entry.
 115      * <p> The last modificatin time may come from zip entry's extensible
 116      * data field {@code NTFS} or {@code Info-ZIP Extended Timestamp}, if
 117      * the entry is read from {@link ZipInputStream} or {@link ZipFile}.
 118      *
 119      * @return the last modification time of the entry, or -1 if not specified









 120      * @see #setTime(long)

 121      */
 122     public long getTime() {














































 123         return mtime;














































































 124     }
 125 
 126     /**
 127      * Sets the uncompressed size of the entry data.

 128      * @param size the uncompressed size in bytes
 129      * @exception IllegalArgumentException if the specified size is less

 130      *            than 0, is greater than 0xFFFFFFFF when
 131      *            <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
 132      *            or is less than 0 when ZIP64 is supported
 133      * @see #getSize()
 134      */
 135     public void setSize(long size) {
 136         if (size < 0) {
 137             throw new IllegalArgumentException("invalid entry size");
 138         }
 139         this.size = size;
 140     }
 141 
 142     /**
 143      * Returns the uncompressed size of the entry data, or -1 if not known.

 144      * @return the uncompressed size of the entry data, or -1 if not known
 145      * @see #setSize(long)
 146      */
 147     public long getSize() {
 148         return size;
 149     }
 150 
 151     /**
 152      * Returns the size of the compressed entry data, or -1 if not known.
 153      * In the case of a stored entry, the compressed size will be the same

 154      * as the uncompressed size of the entry.

 155      * @return the size of the compressed entry data, or -1 if not known
 156      * @see #setCompressedSize(long)
 157      */
 158     public long getCompressedSize() {
 159         return csize;
 160     }
 161 
 162     /**
 163      * Sets the size of the compressed entry data.

 164      * @param csize the compressed size to set to

 165      * @see #getCompressedSize()
 166      */
 167     public void setCompressedSize(long csize) {
 168         this.csize = csize;
 169     }
 170 
 171     /**
 172      * Sets the CRC-32 checksum of the uncompressed entry data.

 173      * @param crc the CRC-32 value
 174      * @exception IllegalArgumentException if the specified CRC-32 value is

 175      *            less than 0 or greater than 0xFFFFFFFF
 176      * @see #getCrc()
 177      */
 178     public void setCrc(long crc) {
 179         if (crc < 0 || crc > 0xFFFFFFFFL) {
 180             throw new IllegalArgumentException("invalid entry crc-32");
 181         }
 182         this.crc = crc;
 183     }
 184 
 185     /**
 186      * Returns the CRC-32 checksum of the uncompressed entry data, or -1 if
 187      * not known.
 188      * @return the CRC-32 checksum of the uncompressed entry data, or -1 if
 189      * not known

 190      * @see #setCrc(long)
 191      */
 192     public long getCrc() {
 193         return crc;
 194     }
 195 
 196     /**
 197      * Sets the compression method for the entry.

 198      * @param method the compression method, either STORED or DEFLATED
 199      * @exception IllegalArgumentException if the specified compression

 200      *            method is invalid
 201      * @see #getMethod()
 202      */
 203     public void setMethod(int method) {
 204         if (method != STORED && method != DEFLATED) {
 205             throw new IllegalArgumentException("invalid compression method");
 206         }
 207         this.method = method;
 208     }
 209 
 210     /**
 211      * Returns the compression method of the entry, or -1 if not specified.

 212      * @return the compression method of the entry, or -1 if not specified
 213      * @see #setMethod(int)
 214      */
 215     public int getMethod() {
 216         return method;
 217     }
 218 
 219     /**
 220      * Sets the optional extra field data for the entry.
 221      * @param extra the extra field data bytes
 222      * @exception IllegalArgumentException if the length of the specified










 223      *            extra field data is greater than 0xFFFF bytes

 224      * @see #getExtra()
 225      */
 226     public void setExtra(byte[] extra) {
 227         if (extra != null && extra.length > 0xFFFF) {













 228             throw new IllegalArgumentException("invalid extra field length");
 229         }

























































 230         this.extra = extra;
 231     }
 232 
 233     /**
 234      * Returns the extra field data for the entry, or null if none.

 235      * @return the extra field data for the entry, or null if none

 236      * @see #setExtra(byte[])
 237      */
 238     public byte[] getExtra() {
 239         return extra;
 240     }
 241 
 242     /**
 243      * Sets the optional comment string for the entry.
 244      *
 245      * <p>ZIP entry comments have maximum length of 0xffff. If the length of the
 246      * specified comment string is greater than 0xFFFF bytes after encoding, only
 247      * the first 0xFFFF bytes are output to the ZIP file entry.
 248      *
 249      * @param comment the comment string
 250      *
 251      * @see #getComment()
 252      */
 253     public void setComment(String comment) {
 254         this.comment = comment;
 255     }
 256 
 257     /**
 258      * Returns the comment string for the entry, or null if none.

 259      * @return the comment string for the entry, or null if none

 260      * @see #setComment(String)
 261      */
 262     public String getComment() {
 263         return comment;
 264     }
 265 
 266     /**
 267      * Returns true if this is a directory entry. A directory entry is
 268      * defined to be one whose name ends with a '/'.
 269      * @return true if this is a directory entry
 270      */
 271     public boolean isDirectory() {
 272         return name.endsWith("/");
 273     }
 274 
 275     /**
 276      * Returns a string representation of the ZIP entry.
 277      */
 278     public String toString() {
 279         return getName();


   1 /*
   2  * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.util.zip;
  27 
  28 import static java.util.zip.ZipUtils.*;
  29 import java.nio.file.attribute.FileTime;
  30 import java.util.Objects;
  31 import java.util.concurrent.TimeUnit;
  32 
  33 /**
  34  * This class is used to represent a ZIP file entry.
  35  *
  36  * @author      David Connelly
  37  */
  38 public
  39 class ZipEntry implements ZipConstants, Cloneable {
  40 
  41     String name;        // entry name
  42     long time = -1;     // last modification time
  43     FileTime mtime;     // last modification time, from extra field data
  44     FileTime atime;     // last access time, from extra field data
  45     FileTime ctime;     // creation time, from extra field data
  46     long crc = -1;      // crc-32 of entry data
  47     long size = -1;     // uncompressed size of entry data
  48     long csize = -1;    // compressed size of entry data
  49     int method = -1;    // compression method
  50     int flag = 0;       // general purpose flag
  51     byte[] extra;       // optional extra field data for entry
  52     String comment;     // optional comment string for entry
  53 
  54     /**
  55      * Compression method for uncompressed entries.
  56      */
  57     public static final int STORED = 0;
  58 
  59     /**
  60      * Compression method for compressed (deflated) entries.
  61      */
  62     public static final int DEFLATED = 8;
  63 
  64     /**
  65      * Creates a new zip entry with the specified name.
  66      *
  67      * @param  name
  68      *         The entry name
  69      *
  70      * @throws NullPointerException if the entry name is null
  71      * @throws IllegalArgumentException if the entry name is longer than
  72      *         0xFFFF bytes
  73      */
  74     public ZipEntry(String name) {
  75         Objects.requireNonNull(name, "name");


  76         if (name.length() > 0xFFFF) {
  77             throw new IllegalArgumentException("entry name too long");
  78         }
  79         this.name = name;
  80     }
  81 
  82     /**
  83      * Creates a new zip entry with fields taken from the specified
  84      * zip entry.
  85      *
  86      * @param  e
  87      *         A zip Entry object
  88      *
  89      * @throws NullPointerException if the entry object is null
  90      */
  91     public ZipEntry(ZipEntry e) {
  92         Objects.requireNonNull(e, "entry");
  93         name = e.name;
  94         time = e.time;
  95         mtime = e.mtime;
  96         atime = e.atime;
  97         ctime = e.ctime;
  98         crc = e.crc;
  99         size = e.size;
 100         csize = e.csize;
 101         method = e.method;
 102         flag = e.flag;
 103         extra = e.extra;
 104         comment = e.comment;
 105     }
 106 
 107     /**
 108      * Creates a new un-initialized zip entry
 109      */
 110     ZipEntry() {}
 111 
 112     /**
 113      * Returns the name of the entry.
 114      * @return the name of the entry
 115      */
 116     public String getName() {
 117         return name;
 118     }
 119 
 120     /**
 121      * Sets the last modification time of the entry.
 122      *
 123      * <p> If the entry is output to a ZIP file or ZIP file formatted
 124      * output stream the last modification time set by this method will
 125      * be stored into the {@code date and time fields} of the zip file
 126      * entry and encoded in standard {@code MS-DOS date and time format}.
 127      * The {@link java.util.TimeZone#getDefault() default TimeZone} is
 128      * used to convert the epoch time to the MS-DOS data and time.
 129      *
 130      * @param  time
 131      *         The last modification time of the entry in milliseconds
 132      *         since the epoch
 133      *
 134      * @see #getTime()
 135      * @see #getLastModifiedTime()
 136      */
 137     public void setTime(long time) {
 138         this.time = time;
 139         this.mtime = null;
 140     }
 141 
 142     /**
 143      * Returns the last modification time of the entry.



 144      *
 145      * <p> If the entry is read from a ZIP file or ZIP file formatted
 146      * input stream, this is the last modification time from the {@code
 147      * date and time fields} of the zip file entry. The
 148      * {@link java.util.TimeZone#getDefault() default TimeZone} is used
 149      * to convert the standard MS-DOS formatted date and time to the
 150      * epoch time.
 151      *
 152      * @return  The last modification time of the entry in milliseconds
 153      *          since the epoch, or -1 if not specified
 154      *
 155      * @see #setTime(long)
 156      * @see #setLastModifiedTime(FileTime)
 157      */
 158     public long getTime() {
 159         return time;
 160     }
 161 
 162     /**
 163      * Sets the last modification time of the entry.
 164      *
 165      * <p> When output to a ZIP file or ZIP file formatted output stream
 166      * the last modification time set by this method will be stored into
 167      * zip file entry's {@code date and time fields} in {@code standard
 168      * MS-DOS date and time format}), and the extended timestamp fields
 169      * in {@code optional extra data} in UTC time.
 170      *
 171      * @param  time
 172      *         The last modification time of the entry
 173      * @return This zip entry
 174      *
 175      * @throws NullPointerException if the {@code time} is null
 176      *
 177      * @see #getLastModifiedTime()
 178      * @since 1.8
 179      */
 180     public ZipEntry setLastModifiedTime(FileTime time) {
 181         Objects.requireNonNull(name, "time");
 182         this.mtime = time;
 183         this.time = time.to(TimeUnit.MILLISECONDS);
 184         return this;
 185     }
 186 
 187     /**
 188      * Returns the last modification time of the entry.
 189      *
 190      * <p> If the entry is read from a ZIP file or ZIP file formatted
 191      * input stream, this is the last modification time from the zip
 192      * file entry's {@code optional extra data} if the extended timestamp
 193      * fields are present. Otherwise the last modification time is read
 194      * from the entry's {@code date and time fields}, the {@link
 195      * java.util.TimeZone#getDefault() default TimeZone} is used to convert
 196      * the standard MS-DOS formatted date and time to the epoch time.
 197      *
 198      * @return The last modification time of the entry, null if not specified
 199      *
 200      * @see #setLastModifiedTime(FileTime)
 201      * @since 1.8
 202      */
 203     public FileTime getLastModifiedTime() {
 204         if (mtime != null)
 205             return mtime;
 206         if (time == -1)
 207             return null;
 208         return FileTime.from(time, TimeUnit.MILLISECONDS);
 209     }
 210 
 211     /**
 212      * Sets the last access time of the entry.
 213      *
 214      * <p> If set, the last access time will be stored into the extended
 215      * timestamp fields of entry's {@code optional extra data}, when output
 216      * to a ZIP file or ZIP file formatted stream.
 217      *
 218      * @param  time
 219      *         The last access time of the entry
 220      * @return This zip entry
 221      *
 222      * @throws NullPointerException if the {@code time} is null
 223      *
 224      * @see #getLastAccessTime()
 225      * @since 1.8
 226      */
 227     public ZipEntry setLastAccessTime(FileTime time) {
 228         Objects.requireNonNull(name, "time");
 229         this.atime = time;
 230         return this;
 231     }
 232 
 233     /**
 234      * Returns the last access time of the entry.
 235      *
 236      * <p> The last access time is from the extended timestamp fields
 237      * of entry's {@code optional extra data} when read from a ZIP file
 238      * or ZIP file formatted stream.
 239      *
 240      * @return The last access time of the entry, null if not specified
 241 
 242      * @see #setLastAccessTime(long)
 243      * @since 1.8
 244      */
 245     public FileTime getLastAccessTime() {
 246         return atime;
 247     }
 248 
 249     /**
 250      * Sets the creation time of the entry.
 251      *
 252      * <p> If set, the creation time will be stored into the extended
 253      * timestamp fields of entry's {@code optional extra data}, when
 254      * output to a ZIP file or ZIP file formatted stream.
 255      *
 256      * @param  time
 257      *         The creation time of the entry
 258      * @return This zip entry
 259      *
 260      * @throws NullPointerException if the {@code time} is null
 261      *
 262      * @see #getCreationTime()
 263      * @since 1.8
 264      */
 265     public ZipEntry setCreationTime(FileTime time) {
 266         Objects.requireNonNull(name, "time");
 267         this.ctime = time;
 268         return this;
 269     }
 270 
 271     /**
 272      * Returns the creation time of the entry.
 273      *
 274      * <p> The creation time is from the extended timestamp fields of
 275      * entry's {@code optional extra data} when read from a ZIP file
 276      * or ZIP file formatted stream.
 277      *
 278      * @return the creation time of the entry, null if not specified
 279      * @see #setCreationTime(FileTime)
 280      * @since 1.8
 281      */
 282     public FileTime getCreationTime() {
 283         return ctime;
 284     }
 285 
 286     /**
 287      * Sets the uncompressed size of the entry data.
 288      *
 289      * @param size the uncompressed size in bytes
 290      *
 291      * @throws IllegalArgumentException if the specified size is less
 292      *         than 0, is greater than 0xFFFFFFFF when
 293      *         <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
 294      *         or is less than 0 when ZIP64 is supported
 295      * @see #getSize()
 296      */
 297     public void setSize(long size) {
 298         if (size < 0) {
 299             throw new IllegalArgumentException("invalid entry size");
 300         }
 301         this.size = size;
 302     }
 303 
 304     /**
 305      * Returns the uncompressed size of the entry data.
 306      *
 307      * @return the uncompressed size of the entry data, or -1 if not known
 308      * @see #setSize(long)
 309      */
 310     public long getSize() {
 311         return size;
 312     }
 313 
 314     /**
 315      * Returns the size of the compressed entry data.
 316      *
 317      * <p> In the case of a stored entry, the compressed size will be the same
 318      * as the uncompressed size of the entry.
 319      *
 320      * @return the size of the compressed entry data, or -1 if not known
 321      * @see #setCompressedSize(long)
 322      */
 323     public long getCompressedSize() {
 324         return csize;
 325     }
 326 
 327     /**
 328      * Sets the size of the compressed entry data.
 329      *
 330      * @param csize the compressed size to set to
 331      *
 332      * @see #getCompressedSize()
 333      */
 334     public void setCompressedSize(long csize) {
 335         this.csize = csize;
 336     }
 337 
 338     /**
 339      * Sets the CRC-32 checksum of the uncompressed entry data.
 340      *
 341      * @param crc the CRC-32 value
 342      *
 343      * @throws IllegalArgumentException if the specified CRC-32 value is
 344      *         less than 0 or greater than 0xFFFFFFFF
 345      * @see #getCrc()
 346      */
 347     public void setCrc(long crc) {
 348         if (crc < 0 || crc > 0xFFFFFFFFL) {
 349             throw new IllegalArgumentException("invalid entry crc-32");
 350         }
 351         this.crc = crc;
 352     }
 353 
 354     /**
 355      * Returns the CRC-32 checksum of the uncompressed entry data.
 356      *
 357      * @return the CRC-32 checksum of the uncompressed entry data, or -1 if
 358      * not known
 359      *
 360      * @see #setCrc(long)
 361      */
 362     public long getCrc() {
 363         return crc;
 364     }
 365 
 366     /**
 367      * Sets the compression method for the entry.
 368      *
 369      * @param method the compression method, either STORED or DEFLATED
 370      *
 371      * @throws  IllegalArgumentException if the specified compression
 372      *          method is invalid
 373      * @see #getMethod()
 374      */
 375     public void setMethod(int method) {
 376         if (method != STORED && method != DEFLATED) {
 377             throw new IllegalArgumentException("invalid compression method");
 378         }
 379         this.method = method;
 380     }
 381 
 382     /**
 383      * Returns the compression method of the entry.
 384      *
 385      * @return the compression method of the entry, or -1 if not specified
 386      * @see #setMethod(int)
 387      */
 388     public int getMethod() {
 389         return method;
 390     }
 391 
 392     /**
 393      * Sets the optional extra field data for the entry.
 394      *
 395      * <p> Invoking this method may change this entry's last modification
 396      * time, last access time and creation time, if the {@code extra} field
 397      * data includes the extensible timestamp fields, such as {@code NTFS tag
 398      * 0x0001} or {@code Info-ZIP Extended Timestamp}, as specified in
 399      * <a href="http://www.info-zip.org/doc/appnote-19970311-iz.zip">Info-ZIP
 400      * Application Note 970311</a>.
 401      *
 402      * @param  extra
 403      *         The extra field data bytes
 404      *
 405      * @throws IllegalArgumentException if the length of the specified
 406      *         extra field data is greater than 0xFFFF bytes
 407      *
 408      * @see #getExtra()
 409      */
 410     public void setExtra(byte[] extra) {
 411         setExtra0(extra, false);
 412     }
 413 
 414     /**
 415      * Sets the optional extra field data for the entry.
 416      *
 417      * @param extra
 418      *        the extra field data bytes
 419      * @param doZIP64
 420      *        if true, set size and csize from ZIP64 fields if present
 421      */
 422     void setExtra0(byte[] extra, boolean doZIP64) {
 423         if (extra != null) {
 424             if (extra.length > 0xFFFF) {
 425                 throw new IllegalArgumentException("invalid extra field length");
 426             }
 427             // extra fields are in "HeaderID(2)DataSize(2)Data... format
 428             int off = 0;
 429             int len = extra.length;
 430             while (off + 4 < len) {
 431                 int tag = get16(extra, off);
 432                 int sz = get16(extra, off + 2);
 433                 off += 4;
 434                 if (off + sz > len)         // invalid data
 435                     break;
 436                 switch (tag) {
 437                 case EXTID_ZIP64:
 438                     if (doZIP64) {
 439                         // LOC extra zip64 entry MUST include BOTH original
 440                         // and compressed file size fields.
 441                         // If invalid zip64 extra fields, simply skip. Even
 442                         // it's rare, it's possible the entry size happens to
 443                         // be the magic value and it "accidently" has some
 444                         // bytes in extra match the id.
 445                         if (sz >= 16) {
 446                             size = get64(extra, off);
 447                             csize = get64(extra, off + 8);
 448                         }
 449                     }
 450                     break;
 451                 case EXTID_NTFS:
 452                     int pos = off + 4;               // reserved 4 bytes
 453                     if (get16(extra, pos) !=  0x0001 || get16(extra, pos + 2) != 24)
 454                         break;
 455                     mtime = winTimeToFileTime(get64(extra, pos + 4));
 456                     atime = winTimeToFileTime(get64(extra, pos + 12));
 457                     ctime = winTimeToFileTime(get64(extra, pos + 20));
 458                     break;
 459                 case EXTID_EXTT:
 460                     int flag = Byte.toUnsignedInt(extra[off]);
 461                     int sz0 = 1;
 462                     // The CEN-header extra field contains the modification
 463                     // time only, or no timestamp at all. 'sz' is used to
 464                     // flag its presence or absence. But if mtime is present
 465                     // in LOC it must be present in CEN as well.
 466                     if ((flag & 0x1) != 0 && (sz0 + 4) <= sz) {
 467                         mtime = unixTimeToFileTime(get32(extra, off + sz0));
 468                         sz0 += 4;
 469                     }
 470                     if ((flag & 0x2) != 0 && (sz0 + 4) <= sz) {
 471                         atime = unixTimeToFileTime(get32(extra, off + sz0));
 472                         sz0 += 4;
 473                     }
 474                     if ((flag & 0x4) != 0 && (sz0 + 4) <= sz) {
 475                         ctime = unixTimeToFileTime(get32(extra, off + sz0));
 476                         sz0 += 4;
 477                     }
 478                     break;
 479                  default:
 480                 }
 481                 off += sz;
 482             }
 483         }
 484         this.extra = extra;
 485     }
 486 
 487     /**
 488      * Returns the extra field data for the entry.
 489      *
 490      * @return the extra field data for the entry, or null if none
 491      *
 492      * @see #setExtra(byte[])
 493      */
 494     public byte[] getExtra() {
 495         return extra;
 496     }
 497 
 498     /**
 499      * Sets the optional comment string for the entry.
 500      *
 501      * <p>ZIP entry comments have maximum length of 0xffff. If the length of the
 502      * specified comment string is greater than 0xFFFF bytes after encoding, only
 503      * the first 0xFFFF bytes are output to the ZIP file entry.
 504      *
 505      * @param comment the comment string
 506      *
 507      * @see #getComment()
 508      */
 509     public void setComment(String comment) {
 510         this.comment = comment;
 511     }
 512 
 513     /**
 514      * Returns the comment string for the entry.
 515      *
 516      * @return the comment string for the entry, or null if none
 517      *
 518      * @see #setComment(String)
 519      */
 520     public String getComment() {
 521         return comment;
 522     }
 523 
 524     /**
 525      * Returns true if this is a directory entry. A directory entry is
 526      * defined to be one whose name ends with a '/'.
 527      * @return true if this is a directory entry
 528      */
 529     public boolean isDirectory() {
 530         return name.endsWith("/");
 531     }
 532 
 533     /**
 534      * Returns a string representation of the ZIP entry.
 535      */
 536     public String toString() {
 537         return getName();