1. /*
  2. * @(#)ZipFile.java 1.41 02/09/30
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.util.zip;
  8. import java.io.InputStream;
  9. import java.io.IOException;
  10. import java.io.EOFException;
  11. import java.io.File;
  12. import java.util.Vector;
  13. import java.util.Enumeration;
  14. import java.util.NoSuchElementException;
  15. import java.security.AccessController;
  16. /**
  17. * This class is used to read entries from a zip file.
  18. *
  19. * @version 1.41, 09/30/02
  20. * @author David Connelly
  21. */
  22. public
  23. class ZipFile implements ZipConstants {
  24. private long jzfile; // address of jzfile data
  25. private String name; // zip file name
  26. private int total; // total number of entries
  27. private static final int STORED = ZipEntry.STORED;
  28. private static final int DEFLATED = ZipEntry.DEFLATED;
  29. static {
  30. AccessController.doPrivileged(
  31. new sun.security.action.LoadLibraryAction("zip"));
  32. initIDs();
  33. }
  34. private static native void initIDs();
  35. /**
  36. * Opens a zip file for reading.
  37. *
  38. * <p>First, if there is a security
  39. * manager, its <code>checkRead</code> method
  40. * is called with the <code>name</code> argument
  41. * as its argument to ensure the read is allowed.
  42. *
  43. * @param name the name of the zip file
  44. * @exception ZipException if a ZIP format error has occurred
  45. * @exception IOException if an I/O error has occurred
  46. * @exception SecurityException if a security manager exists and its
  47. * <code>checkRead</code> method doesn't allow read access to the file.
  48. * @see SecurityManager#checkRead(java.lang.String)
  49. */
  50. public ZipFile(String name) throws IOException {
  51. SecurityManager sm = System.getSecurityManager();
  52. if (sm != null) {
  53. sm.checkRead(name);
  54. }
  55. jzfile = open(name);
  56. this.name = name;
  57. this.total = getTotal(jzfile);
  58. }
  59. private static native long open(String name);
  60. private static native int getTotal(long jzfile);
  61. /**
  62. * Opens a ZIP file for reading given the specified File object.
  63. * @param file the ZIP file to be opened for reading
  64. * @exception ZipException if a ZIP error has occurred
  65. * @exception IOException if an I/O error has occurred
  66. */
  67. public ZipFile(File file) throws ZipException, IOException {
  68. this(file.getPath());
  69. }
  70. /**
  71. * Returns the zip file entry for the specified name, or null
  72. * if not found.
  73. *
  74. * @param name the name of the entry
  75. * @return the zip file entry, or null if not found
  76. * @exception IllegalStateException if the zip file has been closed
  77. */
  78. public ZipEntry getEntry(String name) {
  79. if (name == null) {
  80. throw new NullPointerException("name");
  81. }
  82. if (jzfile == 0) {
  83. throw new IllegalStateException("zip file closed");
  84. }
  85. long jzentry = getEntry(jzfile, name);
  86. if (jzentry != 0) {
  87. return new ZipEntry(name, jzentry);
  88. }
  89. return null;
  90. }
  91. private static native long getEntry(long jzfile, String name);
  92. /**
  93. * Returns an input stream for reading the contents of the specified
  94. * zip file entry.
  95. *
  96. * @param entry the zip file entry
  97. * @exception ZipException if a ZIP format error has occurred
  98. * @exception IOException if an I/O error has occurred
  99. * @exception IllegalStateException if the zip file has been closed
  100. */
  101. public InputStream getInputStream(ZipEntry entry) throws IOException {
  102. return getInputStream(entry.name);
  103. }
  104. /**
  105. * Returns an input stream for reading the contents of the specified
  106. * entry, or null if the entry was not found.
  107. */
  108. private InputStream getInputStream(String name) throws IOException {
  109. if (name == null) {
  110. throw new NullPointerException("name");
  111. }
  112. if (jzfile == 0) {
  113. throw new IllegalStateException("zip file closed");
  114. }
  115. long jzentry = getEntry(jzfile, name);
  116. if (jzentry == 0) {
  117. return null;
  118. }
  119. InputStream in = new ZipFileInputStream(jzfile, jzentry);
  120. switch (getMethod(jzentry)) {
  121. case STORED:
  122. return in;
  123. case DEFLATED:
  124. return new InflaterInputStream(in, getInflater()) {
  125. private boolean isClosed = false;
  126. public void close() {
  127. if (!isClosed) {
  128. releaseInflater(inf);
  129. isClosed = true;
  130. }
  131. }
  132. // Override fill() method to provide an extra "dummy" byte
  133. // at the end of the input stream. This is required when
  134. // using the "nowrap" Inflater option.
  135. protected void fill() throws IOException {
  136. if (eof) {
  137. throw new EOFException(
  138. "Unexpected end of ZLIB input stream");
  139. }
  140. len = this.in.read(buf, 0, buf.length);
  141. if (len == -1) {
  142. buf[0] = 0;
  143. len = 1;
  144. eof = true;
  145. }
  146. inf.setInput(buf, 0, len);
  147. }
  148. private boolean eof;
  149. };
  150. default:
  151. throw new ZipException("invalid compression method");
  152. }
  153. }
  154. private static native int getMethod(long jzentry);
  155. /*
  156. * Gets an inflater from the list of available inflaters or allocates
  157. * a new one.
  158. */
  159. private Inflater getInflater() {
  160. synchronized (inflaters) {
  161. int size = inflaters.size();
  162. if (size > 0) {
  163. Inflater inf = (Inflater)inflaters.remove(size - 1);
  164. inf.reset();
  165. return inf;
  166. } else {
  167. return new Inflater(true);
  168. }
  169. }
  170. }
  171. /*
  172. * Releases the specified inflater to the list of available inflaters.
  173. */
  174. private void releaseInflater(Inflater inf) {
  175. synchronized (inflaters) {
  176. inflaters.add(inf);
  177. }
  178. }
  179. // List of available Inflater objects for decompression
  180. private Vector inflaters = new Vector();
  181. /**
  182. * Returns the path name of the ZIP file.
  183. */
  184. public String getName() {
  185. return name;
  186. }
  187. /**
  188. * Returns an enumeration of the ZIP file entries.
  189. * @exception IllegalStateException if the zip file has been closed
  190. */
  191. public Enumeration entries() {
  192. if (jzfile == 0) {
  193. throw new IllegalStateException("zip file closed");
  194. }
  195. return new Enumeration() {
  196. private int i = 0;
  197. public boolean hasMoreElements() {
  198. return i < total;
  199. }
  200. public Object nextElement() throws NoSuchElementException {
  201. if (i >= total) {
  202. throw new NoSuchElementException();
  203. }
  204. long jzentry = getNextEntry(jzfile, i++);
  205. if (jzentry == 0) {
  206. throw new InternalError("jzentry == 0");
  207. }
  208. return new ZipEntry(jzentry);
  209. }
  210. };
  211. }
  212. private static native long getNextEntry(long jzfile, int i);
  213. /**
  214. * Returns the number of entries in the ZIP file.
  215. * @exception IllegalStateException if the zip file has been closed
  216. */
  217. public int size() {
  218. if (jzfile == 0) {
  219. throw new IllegalStateException("zip file closed");
  220. }
  221. return total;
  222. }
  223. /**
  224. * Closes the ZIP file.
  225. */
  226. public void close() throws IOException {
  227. if (jzfile != 0) {
  228. // Close the zip file
  229. long zf = this.jzfile;
  230. jzfile = 0;
  231. close(zf);
  232. // Release inflaters
  233. synchronized (inflaters) {
  234. int size = inflaters.size();
  235. for (int i = 0; i < size; i++) {
  236. Inflater inf = (Inflater)inflaters.get(i);
  237. inf.end();
  238. }
  239. }
  240. }
  241. }
  242. private static native void close(long jzfile);
  243. /*
  244. * Inner class implementing the input stream used to read a zip file entry.
  245. */
  246. private class ZipFileInputStream extends InputStream {
  247. private long jzentry; // address of jzentry data
  248. private int pos; // current position within entry data
  249. private int rem; // number of remaining bytes within entry
  250. ZipFileInputStream(long jzfile, long jzentry) {
  251. this.jzentry = jzentry;
  252. pos = 0;
  253. rem = getCSize(jzentry);
  254. }
  255. public int read(byte b[], int off, int len) throws IOException {
  256. if (rem == 0) {
  257. return -1;
  258. }
  259. if (len <= 0) {
  260. return 0;
  261. }
  262. if (len > rem) {
  263. len = rem;
  264. }
  265. if (ZipFile.this.jzfile == 0)
  266. throw new ZipException("ZipFile closed.");
  267. len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b, off, len);
  268. if (len > 0) {
  269. pos += len;
  270. rem -= len;
  271. }
  272. return len;
  273. }
  274. public int read() throws IOException {
  275. byte[] b = new byte[1];
  276. if (read(b, 0, 1) == 1) {
  277. return b[0] & 0xff;
  278. } else {
  279. return -1;
  280. }
  281. }
  282. public long skip(long n) {
  283. int len = n > rem ? rem : (int)n;
  284. pos += len;
  285. rem -= len;
  286. return len;
  287. }
  288. public int available() {
  289. return rem;
  290. }
  291. }
  292. private static native int read(long jzfile, long jzentry,
  293. int pos, byte[] b, int off, int len);
  294. private static native int getCSize(long jzentry);
  295. }