ZipFile.java revision fa07c88938c939eea9770e52f4813476e4ba6f3b
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27package java.util.zip; 28 29import java.io.Closeable; 30import java.io.InputStream; 31import java.io.IOException; 32import java.io.EOFException; 33import java.io.File; 34import java.nio.charset.Charset; 35import java.nio.charset.StandardCharsets; 36import java.util.ArrayDeque; 37import java.util.Deque; 38import java.util.Enumeration; 39import java.util.HashMap; 40import java.util.HashSet; 41import java.util.Map; 42import java.util.NoSuchElementException; 43import java.util.Set; 44import java.util.WeakHashMap; 45import java.security.AccessController; 46 47import dalvik.system.CloseGuard; 48import sun.security.action.GetPropertyAction; 49 50import static java.util.zip.ZipConstants64.*; 51 52/** 53 * This class is used to read entries from a zip file. 54 * 55 * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor 56 * or method in this class will cause a {@link NullPointerException} to be 57 * thrown. 58 * 59 * @author David Connelly 60 */ 61public 62class ZipFile implements ZipConstants, Closeable { 63 private long jzfile; // address of jzfile data 64 private final String name; // zip file name 65 private final int total; // total number of entries 66 private final boolean locsig; // if zip file starts with LOCSIG (usually true) 67 private volatile boolean closeRequested = false; 68 69 private final CloseGuard guard = CloseGuard.get(); 70 71 private static final int STORED = ZipEntry.STORED; 72 private static final int DEFLATED = ZipEntry.DEFLATED; 73 74 /** 75 * Mode flag to open a zip file for reading. 76 */ 77 public static final int OPEN_READ = 0x1; 78 79 /** 80 * Mode flag to open a zip file and mark it for deletion. The file will be 81 * deleted some time between the moment that it is opened and the moment 82 * that it is closed, but its contents will remain accessible via the 83 * <tt>ZipFile</tt> object until either the close method is invoked or the 84 * virtual machine exits. 85 */ 86 public static final int OPEN_DELETE = 0x4; 87 88 static { 89 /* Zip library is loaded from System.initializeSystemClass */ 90 initIDs(); 91 } 92 93 private static native void initIDs(); 94 95 private static final boolean usemmap; 96 97 static { 98 // Android-changed: always use mmap. 99 usemmap = true; 100 } 101 102 /** 103 * Opens a zip file for reading. 104 * 105 * <p>First, if there is a security manager, its <code>checkRead</code> 106 * method is called with the <code>name</code> argument as its argument 107 * to ensure the read is allowed. 108 * 109 * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to 110 * decode the entry names and comments. 111 * 112 * @param name the name of the zip file 113 * @throws ZipException if a ZIP format error has occurred 114 * @throws IOException if an I/O error has occurred 115 * @throws SecurityException if a security manager exists and its 116 * <code>checkRead</code> method doesn't allow read access to the file. 117 * 118 * @see SecurityManager#checkRead(java.lang.String) 119 */ 120 public ZipFile(String name) throws IOException { 121 this(new File(name), OPEN_READ); 122 } 123 124 /** 125 * Opens a new <code>ZipFile</code> to read from the specified 126 * <code>File</code> object in the specified mode. The mode argument 127 * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>. 128 * 129 * <p>First, if there is a security manager, its <code>checkRead</code> 130 * method is called with the <code>name</code> argument as its argument to 131 * ensure the read is allowed. 132 * 133 * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to 134 * decode the entry names and comments 135 * 136 * @param file the ZIP file to be opened for reading 137 * @param mode the mode in which the file is to be opened 138 * @throws ZipException if a ZIP format error has occurred 139 * @throws IOException if an I/O error has occurred 140 * @throws SecurityException if a security manager exists and 141 * its <code>checkRead</code> method 142 * doesn't allow read access to the file, 143 * or its <code>checkDelete</code> method doesn't allow deleting 144 * the file when the <tt>OPEN_DELETE</tt> flag is set. 145 * @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid 146 * @see SecurityManager#checkRead(java.lang.String) 147 * @since 1.3 148 */ 149 public ZipFile(File file, int mode) throws IOException { 150 this(file, mode, StandardCharsets.UTF_8); 151 } 152 153 /** 154 * Opens a ZIP file for reading given the specified File object. 155 * 156 * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to 157 * decode the entry names and comments. 158 * 159 * @param file the ZIP file to be opened for reading 160 * @throws ZipException if a ZIP format error has occurred 161 * @throws IOException if an I/O error has occurred 162 */ 163 public ZipFile(File file) throws ZipException, IOException { 164 this(file, OPEN_READ); 165 } 166 167 private ZipCoder zc; 168 169 /** 170 * Opens a new <code>ZipFile</code> to read from the specified 171 * <code>File</code> object in the specified mode. The mode argument 172 * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>. 173 * 174 * <p>First, if there is a security manager, its <code>checkRead</code> 175 * method is called with the <code>name</code> argument as its argument to 176 * ensure the read is allowed. 177 * 178 * @param file the ZIP file to be opened for reading 179 * @param mode the mode in which the file is to be opened 180 * @param charset 181 * the {@linkplain java.nio.charset.Charset charset} to 182 * be used to decode the ZIP entry name and comment that are not 183 * encoded by using UTF-8 encoding (indicated by entry's general 184 * purpose flag). 185 * 186 * @throws ZipException if a ZIP format error has occurred 187 * @throws IOException if an I/O error has occurred 188 * 189 * @throws SecurityException 190 * if a security manager exists and its <code>checkRead</code> 191 * method doesn't allow read access to the file,or its 192 * <code>checkDelete</code> method doesn't allow deleting the 193 * file when the <tt>OPEN_DELETE</tt> flag is set 194 * 195 * @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid 196 * 197 * @see SecurityManager#checkRead(java.lang.String) 198 * 199 * @since 1.7 200 */ 201 public ZipFile(File file, int mode, Charset charset) throws IOException 202 { 203 if (((mode & OPEN_READ) == 0) || 204 ((mode & ~(OPEN_READ | OPEN_DELETE)) != 0)) { 205 throw new IllegalArgumentException("Illegal mode: 0x"+ 206 Integer.toHexString(mode)); 207 } 208 209 // Android-changed: Error out early if the file is too short. 210 if (file.length() < ZipConstants.ENDHDR) { 211 throw new ZipException("File too short to be a zip file: " + file.length()); 212 } 213 String name = file.getPath(); 214 SecurityManager sm = System.getSecurityManager(); 215 if (sm != null) { 216 sm.checkRead(name); 217 if ((mode & OPEN_DELETE) != 0) { 218 sm.checkDelete(name); 219 } 220 } 221 if (charset == null) 222 throw new NullPointerException("charset is null"); 223 this.zc = ZipCoder.get(charset); 224 jzfile = open(name, mode, file.lastModified(), usemmap); 225 this.name = name; 226 this.total = getTotal(jzfile); 227 this.locsig = startsWithLOC(jzfile); 228 Enumeration<? extends ZipEntry> entries = entries(); 229 230 // Android-changed: Error out early if the zipfile has no entries. 231 if (size() == 0 || !entries.hasMoreElements()) { 232 close(); 233 throw new ZipException("No entries"); 234 } 235 236 guard.open("close"); 237 } 238 239 /** 240 * Opens a zip file for reading. 241 * 242 * <p>First, if there is a security manager, its <code>checkRead</code> 243 * method is called with the <code>name</code> argument as its argument 244 * to ensure the read is allowed. 245 * 246 * @param name the name of the zip file 247 * @param charset 248 * the {@linkplain java.nio.charset.Charset charset} to 249 * be used to decode the ZIP entry name and comment that are not 250 * encoded by using UTF-8 encoding (indicated by entry's general 251 * purpose flag). 252 * 253 * @throws ZipException if a ZIP format error has occurred 254 * @throws IOException if an I/O error has occurred 255 * @throws SecurityException 256 * if a security manager exists and its <code>checkRead</code> 257 * method doesn't allow read access to the file 258 * 259 * @see SecurityManager#checkRead(java.lang.String) 260 * 261 * @since 1.7 262 */ 263 public ZipFile(String name, Charset charset) throws IOException 264 { 265 this(new File(name), OPEN_READ, charset); 266 } 267 268 /** 269 * Opens a ZIP file for reading given the specified File object. 270 * @param file the ZIP file to be opened for reading 271 * @param charset 272 * The {@linkplain java.nio.charset.Charset charset} to be 273 * used to decode the ZIP entry name and comment (ignored if 274 * the <a href="package-summary.html#lang_encoding"> language 275 * encoding bit</a> of the ZIP entry's general purpose bit 276 * flag is set). 277 * 278 * @throws ZipException if a ZIP format error has occurred 279 * @throws IOException if an I/O error has occurred 280 * 281 * @since 1.7 282 */ 283 public ZipFile(File file, Charset charset) throws IOException 284 { 285 this(file, OPEN_READ, charset); 286 } 287 288 /** 289 * Returns the zip file comment, or null if none. 290 * 291 * @return the comment string for the zip file, or null if none 292 * 293 * @throws IllegalStateException if the zip file has been closed 294 * 295 * Since 1.7 296 */ 297 public String getComment() { 298 synchronized (this) { 299 ensureOpen(); 300 byte[] bcomm = getCommentBytes(jzfile); 301 if (bcomm == null) 302 return null; 303 return zc.toString(bcomm, bcomm.length); 304 } 305 } 306 307 /** 308 * Returns the zip file entry for the specified name, or null 309 * if not found. 310 * 311 * @param name the name of the entry 312 * @return the zip file entry, or null if not found 313 * @throws IllegalStateException if the zip file has been closed 314 */ 315 public ZipEntry getEntry(String name) { 316 if (name == null) { 317 throw new NullPointerException("name"); 318 } 319 long jzentry = 0; 320 synchronized (this) { 321 ensureOpen(); 322 jzentry = getEntry(jzfile, zc.getBytes(name), true); 323 if (jzentry != 0) { 324 ZipEntry ze = getZipEntry(name, jzentry); 325 freeEntry(jzfile, jzentry); 326 return ze; 327 } 328 } 329 return null; 330 } 331 332 private static native long getEntry(long jzfile, byte[] name, 333 boolean addSlash); 334 335 // freeEntry releases the C jzentry struct. 336 private static native void freeEntry(long jzfile, long jzentry); 337 338 // the outstanding inputstreams that need to be closed, 339 // mapped to the inflater objects they use. 340 private final Map<InputStream, Inflater> streams = new WeakHashMap<>(); 341 342 /** 343 * Returns an input stream for reading the contents of the specified 344 * zip file entry. 345 * 346 * <p> Closing this ZIP file will, in turn, close all input 347 * streams that have been returned by invocations of this method. 348 * 349 * @param entry the zip file entry 350 * @return the input stream for reading the contents of the specified 351 * zip file entry. 352 * @throws ZipException if a ZIP format error has occurred 353 * @throws IOException if an I/O error has occurred 354 * @throws IllegalStateException if the zip file has been closed 355 */ 356 public InputStream getInputStream(ZipEntry entry) throws IOException { 357 if (entry == null) { 358 throw new NullPointerException("entry"); 359 } 360 long jzentry = 0; 361 ZipFileInputStream in = null; 362 synchronized (this) { 363 ensureOpen(); 364 if (!zc.isUTF8() && (entry.flag & EFS) != 0) { 365 jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), true); 366 } else { 367 jzentry = getEntry(jzfile, zc.getBytes(entry.name), true); 368 } 369 if (jzentry == 0) { 370 return null; 371 } 372 in = new ZipFileInputStream(jzentry); 373 374 switch (getEntryMethod(jzentry)) { 375 case STORED: 376 synchronized (streams) { 377 streams.put(in, null); 378 } 379 return in; 380 case DEFLATED: 381 // MORE: Compute good size for inflater stream: 382 long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack 383 if (size > 65536) size = 8192; 384 if (size <= 0) size = 4096; 385 Inflater inf = getInflater(); 386 InputStream is = 387 new ZipFileInflaterInputStream(in, inf, (int)size); 388 synchronized (streams) { 389 streams.put(is, inf); 390 } 391 return is; 392 default: 393 throw new ZipException("invalid compression method"); 394 } 395 } 396 } 397 398 private class ZipFileInflaterInputStream extends InflaterInputStream { 399 private volatile boolean closeRequested = false; 400 private boolean eof = false; 401 private final ZipFileInputStream zfin; 402 403 ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf, 404 int size) { 405 super(zfin, inf, size); 406 this.zfin = zfin; 407 } 408 409 public void close() throws IOException { 410 if (closeRequested) 411 return; 412 closeRequested = true; 413 414 super.close(); 415 Inflater inf; 416 synchronized (streams) { 417 inf = streams.remove(this); 418 } 419 if (inf != null) { 420 releaseInflater(inf); 421 } 422 } 423 424 // Override fill() method to provide an extra "dummy" byte 425 // at the end of the input stream. This is required when 426 // using the "nowrap" Inflater option. 427 protected void fill() throws IOException { 428 if (eof) { 429 throw new EOFException("Unexpected end of ZLIB input stream"); 430 } 431 len = in.read(buf, 0, buf.length); 432 if (len == -1) { 433 buf[0] = 0; 434 len = 1; 435 eof = true; 436 } 437 inf.setInput(buf, 0, len); 438 } 439 440 public int available() throws IOException { 441 if (closeRequested) 442 return 0; 443 long avail = zfin.size() - inf.getBytesWritten(); 444 return (avail > (long) Integer.MAX_VALUE ? 445 Integer.MAX_VALUE : (int) avail); 446 } 447 448 protected void finalize() throws Throwable { 449 close(); 450 } 451 } 452 453 /* 454 * Gets an inflater from the list of available inflaters or allocates 455 * a new one. 456 */ 457 private Inflater getInflater() { 458 Inflater inf; 459 synchronized (inflaterCache) { 460 while (null != (inf = inflaterCache.poll())) { 461 if (false == inf.ended()) { 462 return inf; 463 } 464 } 465 } 466 return new Inflater(true); 467 } 468 469 /* 470 * Releases the specified inflater to the list of available inflaters. 471 */ 472 private void releaseInflater(Inflater inf) { 473 if (false == inf.ended()) { 474 inf.reset(); 475 synchronized (inflaterCache) { 476 inflaterCache.add(inf); 477 } 478 } 479 } 480 481 // List of available Inflater objects for decompression 482 private Deque<Inflater> inflaterCache = new ArrayDeque<>(); 483 484 /** 485 * Returns the path name of the ZIP file. 486 * @return the path name of the ZIP file 487 */ 488 public String getName() { 489 return name; 490 } 491 492 /** 493 * Returns an enumeration of the ZIP file entries. 494 * @return an enumeration of the ZIP file entries 495 * @throws IllegalStateException if the zip file has been closed 496 */ 497 public Enumeration<? extends ZipEntry> entries() { 498 ensureOpen(); 499 return new Enumeration<ZipEntry>() { 500 private int i = 0; 501 public boolean hasMoreElements() { 502 synchronized (ZipFile.this) { 503 ensureOpen(); 504 return i < total; 505 } 506 } 507 public ZipEntry nextElement() throws NoSuchElementException { 508 synchronized (ZipFile.this) { 509 ensureOpen(); 510 if (i >= total) { 511 throw new NoSuchElementException(); 512 } 513 long jzentry = getNextEntry(jzfile, i++); 514 if (jzentry == 0) { 515 String message; 516 if (closeRequested) { 517 message = "ZipFile concurrently closed"; 518 } else { 519 message = getZipMessage(ZipFile.this.jzfile); 520 } 521 throw new ZipError("jzentry == 0" + 522 ",\n jzfile = " + ZipFile.this.jzfile + 523 ",\n total = " + ZipFile.this.total + 524 ",\n name = " + ZipFile.this.name + 525 ",\n i = " + i + 526 ",\n message = " + message 527 ); 528 } 529 ZipEntry ze = getZipEntry(null, jzentry); 530 freeEntry(jzfile, jzentry); 531 return ze; 532 } 533 } 534 }; 535 } 536 537 private ZipEntry getZipEntry(String name, long jzentry) { 538 ZipEntry e = new ZipEntry(); 539 e.flag = getEntryFlag(jzentry); // get the flag first 540 if (name != null) { 541 e.name = name; 542 } else { 543 byte[] bname = getEntryBytes(jzentry, JZENTRY_NAME); 544 if (!zc.isUTF8() && (e.flag & EFS) != 0) { 545 e.name = zc.toStringUTF8(bname, bname.length); 546 } else { 547 e.name = zc.toString(bname, bname.length); 548 } 549 } 550 e.time = getEntryTime(jzentry); 551 e.crc = getEntryCrc(jzentry); 552 e.size = getEntrySize(jzentry); 553 e. csize = getEntryCSize(jzentry); 554 e.method = getEntryMethod(jzentry); 555 e.extra = getEntryBytes(jzentry, JZENTRY_EXTRA); 556 byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT); 557 if (bcomm == null) { 558 e.comment = null; 559 } else { 560 if (!zc.isUTF8() && (e.flag & EFS) != 0) { 561 e.comment = zc.toStringUTF8(bcomm, bcomm.length); 562 } else { 563 e.comment = zc.toString(bcomm, bcomm.length); 564 } 565 } 566 return e; 567 } 568 569 private static native long getNextEntry(long jzfile, int i); 570 571 /** 572 * Returns the number of entries in the ZIP file. 573 * @return the number of entries in the ZIP file 574 * @throws IllegalStateException if the zip file has been closed 575 */ 576 public int size() { 577 ensureOpen(); 578 return total; 579 } 580 581 /** 582 * Closes the ZIP file. 583 * <p> Closing this ZIP file will close all of the input streams 584 * previously returned by invocations of the {@link #getInputStream 585 * getInputStream} method. 586 * 587 * @throws IOException if an I/O error has occurred 588 */ 589 public void close() throws IOException { 590 if (closeRequested) 591 return; 592 guard.close(); 593 closeRequested = true; 594 595 synchronized (this) { 596 // Close streams, release their inflaters 597 synchronized (streams) { 598 if (false == streams.isEmpty()) { 599 Map<InputStream, Inflater> copy = new HashMap<>(streams); 600 streams.clear(); 601 for (Map.Entry<InputStream, Inflater> e : copy.entrySet()) { 602 e.getKey().close(); 603 Inflater inf = e.getValue(); 604 if (inf != null) { 605 inf.end(); 606 } 607 } 608 } 609 } 610 611 // Release cached inflaters 612 Inflater inf; 613 synchronized (inflaterCache) { 614 while (null != (inf = inflaterCache.poll())) { 615 inf.end(); 616 } 617 } 618 619 if (jzfile != 0) { 620 // Close the zip file 621 long zf = this.jzfile; 622 jzfile = 0; 623 624 close(zf); 625 } 626 } 627 } 628 629 /** 630 * Ensures that the system resources held by this ZipFile object are 631 * released when there are no more references to it. 632 * 633 * <p> 634 * Since the time when GC would invoke this method is undetermined, 635 * it is strongly recommended that applications invoke the <code>close</code> 636 * method as soon they have finished accessing this <code>ZipFile</code>. 637 * This will prevent holding up system resources for an undetermined 638 * length of time. 639 * 640 * @throws IOException if an I/O error has occurred 641 * @see java.util.zip.ZipFile#close() 642 */ 643 protected void finalize() throws IOException { 644 if (guard != null) { 645 guard.warnIfOpen(); 646 } 647 648 close(); 649 } 650 651 private static native void close(long jzfile); 652 653 private void ensureOpen() { 654 if (closeRequested) { 655 throw new IllegalStateException("zip file closed"); 656 } 657 658 if (jzfile == 0) { 659 throw new IllegalStateException("The object is not initialized."); 660 } 661 } 662 663 private void ensureOpenOrZipException() throws IOException { 664 if (closeRequested) { 665 throw new ZipException("ZipFile closed"); 666 } 667 } 668 669 /* 670 * Inner class implementing the input stream used to read a 671 * (possibly compressed) zip file entry. 672 */ 673 private class ZipFileInputStream extends InputStream { 674 private volatile boolean closeRequested = false; 675 protected long jzentry; // address of jzentry data 676 private long pos; // current position within entry data 677 protected long rem; // number of remaining bytes within entry 678 protected long size; // uncompressed size of this entry 679 680 ZipFileInputStream(long jzentry) { 681 pos = 0; 682 rem = getEntryCSize(jzentry); 683 size = getEntrySize(jzentry); 684 this.jzentry = jzentry; 685 } 686 687 public int read(byte b[], int off, int len) throws IOException { 688 // Android-changed : Always throw an exception on read if the zipfile 689 // has already been closed. 690 ensureOpenOrZipException(); 691 692 if (rem == 0) { 693 return -1; 694 } 695 if (len <= 0) { 696 return 0; 697 } 698 if (len > rem) { 699 len = (int) rem; 700 } 701 synchronized (ZipFile.this) { 702 len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b, 703 off, len); 704 } 705 if (len > 0) { 706 pos += len; 707 rem -= len; 708 } 709 if (rem == 0) { 710 close(); 711 } 712 return len; 713 } 714 715 public int read() throws IOException { 716 byte[] b = new byte[1]; 717 if (read(b, 0, 1) == 1) { 718 return b[0] & 0xff; 719 } else { 720 return -1; 721 } 722 } 723 724 public long skip(long n) { 725 if (n > rem) 726 n = rem; 727 pos += n; 728 rem -= n; 729 if (rem == 0) { 730 close(); 731 } 732 return n; 733 } 734 735 public int available() { 736 return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem; 737 } 738 739 public long size() { 740 return size; 741 } 742 743 public void close() { 744 if (closeRequested) 745 return; 746 closeRequested = true; 747 748 rem = 0; 749 synchronized (ZipFile.this) { 750 if (jzentry != 0 && ZipFile.this.jzfile != 0) { 751 freeEntry(ZipFile.this.jzfile, jzentry); 752 jzentry = 0; 753 } 754 } 755 synchronized (streams) { 756 streams.remove(this); 757 } 758 } 759 760 protected void finalize() { 761 close(); 762 } 763 } 764 765 /** 766 * Returns {@code true} if, and only if, the zip file begins with {@code 767 * LOCSIG}. 768 * 769 * @hide 770 */ 771 public boolean startsWithLocHeader() { 772 return locsig; 773 } 774 775 private static native long open(String name, int mode, long lastModified, 776 boolean usemmap) throws IOException; 777 private static native int getTotal(long jzfile); 778 private static native boolean startsWithLOC(long jzfile); 779 private static native int read(long jzfile, long jzentry, 780 long pos, byte[] b, int off, int len); 781 782 // access to the native zentry object 783 private static native long getEntryTime(long jzentry); 784 private static native long getEntryCrc(long jzentry); 785 private static native long getEntryCSize(long jzentry); 786 private static native long getEntrySize(long jzentry); 787 private static native int getEntryMethod(long jzentry); 788 private static native int getEntryFlag(long jzentry); 789 private static native byte[] getCommentBytes(long jzfile); 790 791 private static final int JZENTRY_NAME = 0; 792 private static final int JZENTRY_EXTRA = 1; 793 private static final int JZENTRY_COMMENT = 2; 794 private static native byte[] getEntryBytes(long jzentry, int type); 795 796 private static native String getZipMessage(long jzfile); 797} 798