6 * Some portions of this file have been modified by Robert Hanson hansonr.at.stolaf.edu 2012-2017
7 * for use in SwingJS via transpilation into JavaScript using Java2Script.
9 * Copyright (C) 2006 The Jmol Development Team
11 * Contact: jmol-developers@lists.sf.net
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 import java.io.BufferedInputStream;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.OutputStream;
36 import java.util.zip.CRC32;
37 import java.util.zip.GZIPInputStream;
38 import java.util.zip.ZipEntry;
39 import java.util.zip.ZipInputStream;
40 import java.util.zip.ZipOutputStream;
42 import javajs.api.GenericZipInputStream;
43 import javajs.api.GenericZipTools;
44 import javajs.api.Interface;
45 import javajs.api.ZInputStream;
47 import org.apache.tools.bzip2.CBZip2InputStreamFactory;
51 * Note the JSmol/HTML5 must use its own version of java.util.zip.ZipOutputStream.
54 public class ZipTools implements GenericZipTools {
61 public ZInputStream newZipInputStream(InputStream is) {
65 @SuppressWarnings("resource")
66 private static ZInputStream newZIS(InputStream is) {
67 return (is instanceof ZInputStream ? (ZInputStream) is
68 : is instanceof BufferedInputStream ? new GenericZipInputStream(is)
69 : new GenericZipInputStream(new BufferedInputStream(is)));
73 * reads a ZIP file and saves all data in a Hashtable so that the files may be
74 * organized later in a different order. Also adds a #Directory_Listing entry.
76 * Files are bracketed by BEGIN Directory Entry and END Directory Entry lines,
77 * similar to CompoundDocument.getAllData.
82 * prefix for entry listing
83 * @param binaryFileList
84 * |-separated list of files that should be saved as xx xx xx hex byte
85 * strings. The directory listing is appended with ":asBinaryString"
89 public void getAllZipData(InputStream is, String[] subfileList,
90 String name0, String binaryFileList, String exclude,
91 Map<String, String> fileData) {
92 ZipInputStream zis = (ZipInputStream) newZIS(is);
94 SB listing = new SB();
95 binaryFileList = "|" + binaryFileList + "|";
96 String prefix = PT.join(subfileList, '/', 1);
97 String prefixd = null;
99 prefixd = prefix.substring(0, prefix.indexOf("/") + 1);
100 if (prefixd.length() == 0)
104 while ((ze = zis.getNextEntry()) != null) {
105 String name = ze.getName();
106 if (prefix != null && prefixd != null
107 && !(name.equals(prefix) || name.startsWith(prefixd))
108 || exclude != null && name.contains(exclude))
110 //System.out.println("ziputil: " + name);
111 listing.append(name).appendC('\n');
112 String sname = "|" + name.substring(name.lastIndexOf("/") + 1) + "|";
113 boolean asBinaryString = (binaryFileList.indexOf(sname) >= 0);
114 byte[] bytes = Rdr.getLimitedStreamBytes(zis, ze.getSize());
116 if (asBinaryString) {
117 str = getBinaryStringForBytes(bytes);
118 name += ":asBinaryString";
120 str = Rdr.fixUTF(bytes);
122 str = "BEGIN Directory Entry " + name + "\n" + str
123 + "\nEND Directory Entry " + name + "\n";
124 String key = name0 + "|" + name;
125 fileData.put(key, str);
127 } catch (Exception e) {
129 fileData.put("#Directory_Listing", listing.toString());
132 private String getBinaryStringForBytes(byte[] bytes) {
134 for (int i = 0; i < bytes.length; i++)
135 ret.append(Integer.toHexString(bytes[i] & 0xFF)).appendC(' ');
136 return ret.toString();
140 * iteratively drills into zip files of zip files to extract file content or
141 * zip file directory. Also works with JAR files.
143 * Does not return "__MACOS" paths
148 * @param asBufferedInputStream
150 * @return directory listing or subfile contents
153 public Object getZipFileDirectory(BufferedInputStream bis, String[] list,
154 int listPtr, boolean asBufferedInputStream) {
156 if (list == null || listPtr >= list.length)
157 return getZipDirectoryAsStringAndClose(bis);
158 bis = Rdr.getPngZipStream(bis, true);
159 String fileName = list[listPtr];
160 ZipInputStream zis = new ZipInputStream(bis);
162 //System.out.println("fname=" + fileName);
164 boolean isAll = (fileName.equals("."));
165 if (isAll || fileName.lastIndexOf("/") == fileName.length() - 1) {
167 while ((ze = zis.getNextEntry()) != null) {
168 String name = ze.getName();
169 if (isAll || name.startsWith(fileName))
170 ret.append(name).appendC('\n');
172 String str = ret.toString();
173 return (asBufferedInputStream ? Rdr.getBIS(str.getBytes()) : str);
175 int pt = fileName.indexOf(":asBinaryString");
176 boolean asBinaryString = (pt > 0);
178 fileName = fileName.substring(0, pt);
179 fileName = fileName.replace('\\', '/');
180 while ((ze = zis.getNextEntry()) != null
181 && !fileName.equals(ze.getName())) {
183 byte[] bytes = (ze == null ? null : Rdr.getLimitedStreamBytes(zis,
189 if (Rdr.isZipB(bytes) || Rdr.isPngZipB(bytes))
190 return getZipFileDirectory(Rdr.getBIS(bytes), list, ++listPtr,
191 asBufferedInputStream);
192 if (asBufferedInputStream)
193 return Rdr.getBIS(bytes);
194 if (asBinaryString) {
196 for (int i = 0; i < bytes.length; i++)
197 ret.append(Integer.toHexString(bytes[i] & 0xFF)).appendC(' ');
198 return ret.toString();
200 if (Rdr.isGzipB(bytes))
201 bytes = Rdr.getLimitedStreamBytes(getUnGzippedInputStream(bytes), -1);
202 return Rdr.fixUTF(bytes);
203 } catch (Exception e) {
209 public byte[] getZipFileContentsAsBytes(BufferedInputStream bis,
210 String[] list, int listPtr) {
211 byte[] ret = new byte[0];
212 String fileName = list[listPtr];
213 if (fileName.lastIndexOf("/") == fileName.length() - 1)
216 bis = Rdr.getPngZipStream(bis, true);
217 ZipInputStream zis = new ZipInputStream(bis);
219 while ((ze = zis.getNextEntry()) != null) {
220 if (!fileName.equals(ze.getName()))
222 byte[] bytes = Rdr.getLimitedStreamBytes(zis, ze.getSize());
223 return ((Rdr.isZipB(bytes) || Rdr.isPngZipB(bytes)) && ++listPtr < list.length ? getZipFileContentsAsBytes(
224 Rdr.getBIS(bytes), list, listPtr) : bytes);
226 } catch (Exception e) {
232 public String getZipDirectoryAsStringAndClose(BufferedInputStream bis) {
234 String[] s = new String[0];
236 s = getZipDirectoryOrErrorAndClose(bis, null);
238 } catch (Exception e) {
239 System.out.println(e.toString());
241 for (int i = 0; i < s.length; i++)
242 sb.append(s[i]).appendC('\n');
243 return sb.toString();
247 public String[] getZipDirectoryAndClose(BufferedInputStream bis,
249 String[] s = new String[0];
251 s = getZipDirectoryOrErrorAndClose(bis, manifestID);
253 } catch (Exception e) {
254 System.out.println(e.toString());
259 private String[] getZipDirectoryOrErrorAndClose(BufferedInputStream bis,
262 bis = Rdr.getPngZipStream(bis, true);
263 Lst<String> v = new Lst<String>();
264 ZipInputStream zis = new ZipInputStream(bis);
266 String manifest = null;
267 while ((ze = zis.getNextEntry()) != null) {
268 String fileName = ze.getName();
269 if (manifestID != null && fileName.startsWith(manifestID))
270 manifest = getStreamAsString(zis);
271 else if (!fileName.startsWith("__MACOS")) // resource fork not nec.
275 if (manifestID != null)
276 v.add(0, manifest == null ? "" : manifest + "\n############\n");
277 return v.toArray(new String[v.size()]);
280 public static String getStreamAsString(InputStream is) throws IOException {
281 return Rdr.fixUTF(Rdr.getLimitedStreamBytes(is, -1));
285 public InputStream newGZIPInputStream(InputStream is) throws IOException {
286 return new BufferedInputStream(new GZIPInputStream(is, 512));
290 public InputStream newBZip2InputStream(InputStream is) throws IOException {
291 return new BufferedInputStream(((CBZip2InputStreamFactory) Interface.getInterface("org.apache.tools.bzip2.CBZip2InputStreamFactory")).getStream(is));
295 public BufferedInputStream getUnGzippedInputStream(byte[] bytes) {
297 return Rdr.getUnzippedInputStream(this, Rdr.getBIS(bytes));
298 } catch (Exception e) {
304 public void addZipEntry(Object zos, String fileName) throws IOException {
305 ((ZipOutputStream) zos).putNextEntry(new ZipEntry(fileName));
309 public void closeZipEntry(Object zos) throws IOException {
310 ((ZipOutputStream) zos).closeEntry();
314 public Object getZipOutputStream(Object bos) {
318 // * return javajs.api.Interface.getInterface(
319 // * "java.util.zip.ZipOutputStream").setZOS(bos);
323 return new ZipOutputStream((OutputStream) bos);
328 public int getCrcValue(byte[] bytes) {
329 CRC32 crc = new CRC32();
330 crc.update(bytes, 0, bytes.length);
331 return (int) crc.getValue();
335 public void readFileAsMap(BufferedInputStream bis, Map<String, Object> bdata, String name) {
336 readFileAsMapStatic(bis, bdata, name);
339 public static void readFileAsMapStatic(BufferedInputStream bis,
340 Map<String, Object> bdata, String name) {
341 int pt = (name == null ? -1 : name.indexOf("|"));
342 name = (pt >= 0 ? name.substring(pt + 1) : null);
344 if (Rdr.isPngZipStream(bis)) {
345 boolean isImage = "_IMAGE_".equals(name);
346 if (name == null || isImage)
347 bdata.put((isImage ? "_DATA_" : "_IMAGE_"), new BArray(getPngImageBytes(bis)));
349 cacheZipContentsStatic(bis, name, bdata, true);
350 } else if (Rdr.isZipS(bis)) {
351 cacheZipContentsStatic(bis, name, bdata, true);
352 } else if (name == null){
353 bdata.put("_DATA_", new BArray(Rdr.getLimitedStreamBytes(bis, -1)));
355 throw new IOException("ZIP file " + name + " not found");
357 bdata.put("$_BINARY_$", Boolean.TRUE);
358 } catch (IOException e) {
360 bdata.put("_ERROR_", e.getMessage());
365 public String cacheZipContents(BufferedInputStream bis,
367 Map<String, Object> cache,
368 boolean asByteArray) {
369 return cacheZipContentsStatic(bis, fileName, cache, asByteArray);
375 * @param fileName may end with "/" for a prefix or contain "|xxxx.xxx" for a specific file or be null
380 public static String cacheZipContentsStatic(BufferedInputStream bis,
381 String fileName, Map<String, Object> cache, boolean asByteArray) {
382 ZipInputStream zis = (ZipInputStream) newZIS(bis);
384 SB listing = new SB();
386 boolean isPath = (fileName != null && fileName.endsWith("/"));
387 boolean oneFile = (asByteArray && !isPath && fileName != null);
388 int pt = (oneFile ? fileName.indexOf("|") : -1);
389 String file0 = (pt >= 0 ? fileName : null);
391 fileName = fileName.substring(0, pt);
392 String prefix = (fileName == null ? "" : isPath ? fileName : fileName + "|");
394 while ((ze = zis.getNextEntry()) != null) {
395 String name = ze.getName();
396 if (fileName != null) {
398 if (!name.equalsIgnoreCase(fileName))
401 listing.append(name).appendC('\n');
404 long nBytes = ze.getSize();
405 byte[] bytes = Rdr.getLimitedStreamBytes(zis, nBytes);
407 readFileAsMapStatic(Rdr.getBIS(bytes), cache, file0);
411 Object o = (asByteArray ? new BArray(bytes) : bytes);
412 cache.put((oneFile ? "_DATA_" : prefix + name), o);
417 } catch (Exception e) {
420 } catch (IOException e1) {
424 if (n == 0 || fileName == null)
426 System.out.println("ZipTools cached " + n + " bytes from " + fileName);
427 return listing.toString();
430 private static byte[] getPngImageBytes(BufferedInputStream bis) {
432 if (Rdr.isPngZipStream(bis)) {
433 int pt_count[] = new int[2];
434 Rdr.getPngZipPointAndCount(bis, pt_count);
435 if (pt_count[1] != 0)
436 return deActivatePngZipB(Rdr.getLimitedStreamBytes(bis, pt_count[0]));
438 return Rdr.getLimitedStreamBytes(bis, -1);
439 } catch (IOException e) {
445 * Once a PNGJ image has been extracted, we want to red-line its
446 * iTXt "Jmol Type PNGJ" tag, since it is no longer associated with
450 * @return disfigured bytes
453 private static byte[] deActivatePngZipB(byte[] bytes) {
454 // \0PNGJ starting at byte 50 changed to \0 NGJ
455 if (Rdr.isPngZipB(bytes))