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 javajs.J2SIgnoreImport;
37 import javajs.api.GenericZipInputStream;
38 import javajs.api.GenericZipTools;
39 import javajs.api.ZInputStream;
42 import java.util.zip.CRC32;
43 import java.util.zip.GZIPInputStream;
44 import java.util.zip.ZipEntry;
45 import java.util.zip.ZipInputStream;
46 import java.util.zip.ZipOutputStream;
50 * Note the JSmol/HTML5 must use its own version of java.util.zip.ZipOutputStream.
53 @J2SIgnoreImport({ java.util.zip.ZipOutputStream.class })
54 public class ZipTools implements GenericZipTools {
61 public ZInputStream newZipInputStream(InputStream is) {
65 private static ZInputStream newZIS(InputStream is) {
66 return (is instanceof ZInputStream ? (ZInputStream) is
67 : is instanceof BufferedInputStream ? new GenericZipInputStream(is)
68 : new GenericZipInputStream(new BufferedInputStream(is)));
72 * reads a ZIP file and saves all data in a Hashtable so that the files may be
73 * organized later in a different order. Also adds a #Directory_Listing entry.
75 * Files are bracketed by BEGIN Directory Entry and END Directory Entry lines,
76 * similar to CompoundDocument.getAllData.
81 * prefix for entry listing
82 * @param binaryFileList
83 * |-separated list of files that should be saved as xx xx xx hex byte
84 * strings. The directory listing is appended with ":asBinaryString"
88 public void getAllZipData(InputStream is, String[] subfileList,
89 String name0, String binaryFileList, String exclude,
90 Map<String, String> fileData) {
91 ZipInputStream zis = (ZipInputStream) newZIS(is);
93 SB listing = new SB();
94 binaryFileList = "|" + binaryFileList + "|";
95 String prefix = PT.join(subfileList, '/', 1);
96 String prefixd = null;
98 prefixd = prefix.substring(0, prefix.indexOf("/") + 1);
99 if (prefixd.length() == 0)
103 while ((ze = zis.getNextEntry()) != null) {
104 String name = ze.getName();
105 if (prefix != null && prefixd != null
106 && !(name.equals(prefix) || name.startsWith(prefixd))
107 || exclude != null && name.contains(exclude))
109 //System.out.println("ziputil: " + name);
110 listing.append(name).appendC('\n');
111 String sname = "|" + name.substring(name.lastIndexOf("/") + 1) + "|";
112 boolean asBinaryString = (binaryFileList.indexOf(sname) >= 0);
113 byte[] bytes = Rdr.getLimitedStreamBytes(zis, ze.getSize());
115 if (asBinaryString) {
116 str = getBinaryStringForBytes(bytes);
117 name += ":asBinaryString";
119 str = Rdr.fixUTF(bytes);
121 str = "BEGIN Directory Entry " + name + "\n" + str
122 + "\nEND Directory Entry " + name + "\n";
123 String key = name0 + "|" + name;
124 fileData.put(key, str);
126 } catch (Exception e) {
128 fileData.put("#Directory_Listing", listing.toString());
131 private String getBinaryStringForBytes(byte[] bytes) {
133 for (int i = 0; i < bytes.length; i++)
134 ret.append(Integer.toHexString(bytes[i] & 0xFF)).appendC(' ');
135 return ret.toString();
139 * iteratively drills into zip files of zip files to extract file content or
140 * zip file directory. Also works with JAR files.
142 * Does not return "__MACOS" paths
147 * @param asBufferedInputStream
149 * @return directory listing or subfile contents
152 public Object getZipFileDirectory(BufferedInputStream bis, String[] list,
153 int listPtr, boolean asBufferedInputStream) {
155 if (list == null || listPtr >= list.length)
156 return getZipDirectoryAsStringAndClose(bis);
157 bis = Rdr.getPngZipStream(bis, true);
158 String fileName = list[listPtr];
159 ZipInputStream zis = new ZipInputStream(bis);
161 //System.out.println("fname=" + fileName);
163 boolean isAll = (fileName.equals("."));
164 if (isAll || fileName.lastIndexOf("/") == fileName.length() - 1) {
166 while ((ze = zis.getNextEntry()) != null) {
167 String name = ze.getName();
168 if (isAll || name.startsWith(fileName))
169 ret.append(name).appendC('\n');
171 String str = ret.toString();
172 return (asBufferedInputStream ? Rdr.getBIS(str.getBytes()) : str);
174 int pt = fileName.indexOf(":asBinaryString");
175 boolean asBinaryString = (pt > 0);
177 fileName = fileName.substring(0, pt);
178 fileName = fileName.replace('\\', '/');
179 while ((ze = zis.getNextEntry()) != null
180 && !fileName.equals(ze.getName())) {
182 byte[] bytes = (ze == null ? null : Rdr.getLimitedStreamBytes(zis,
188 if (Rdr.isZipB(bytes) || Rdr.isPngZipB(bytes))
189 return getZipFileDirectory(Rdr.getBIS(bytes), list, ++listPtr,
190 asBufferedInputStream);
191 if (asBufferedInputStream)
192 return Rdr.getBIS(bytes);
193 if (asBinaryString) {
195 for (int i = 0; i < bytes.length; i++)
196 ret.append(Integer.toHexString(bytes[i] & 0xFF)).appendC(' ');
197 return ret.toString();
199 if (Rdr.isGzipB(bytes))
200 bytes = Rdr.getLimitedStreamBytes(getUnGzippedInputStream(bytes), -1);
201 return Rdr.fixUTF(bytes);
202 } catch (Exception e) {
208 public byte[] getZipFileContentsAsBytes(BufferedInputStream bis,
209 String[] list, int listPtr) {
210 byte[] ret = new byte[0];
211 String fileName = list[listPtr];
212 if (fileName.lastIndexOf("/") == fileName.length() - 1)
215 bis = Rdr.getPngZipStream(bis, true);
216 ZipInputStream zis = new ZipInputStream(bis);
218 while ((ze = zis.getNextEntry()) != null) {
219 if (!fileName.equals(ze.getName()))
221 byte[] bytes = Rdr.getLimitedStreamBytes(zis, ze.getSize());
222 return ((Rdr.isZipB(bytes) || Rdr.isPngZipB(bytes)) && ++listPtr < list.length ? getZipFileContentsAsBytes(
223 Rdr.getBIS(bytes), list, listPtr) : bytes);
225 } catch (Exception e) {
231 public String getZipDirectoryAsStringAndClose(BufferedInputStream bis) {
233 String[] s = new String[0];
235 s = getZipDirectoryOrErrorAndClose(bis, null);
237 } catch (Exception e) {
238 System.out.println(e.toString());
240 for (int i = 0; i < s.length; i++)
241 sb.append(s[i]).appendC('\n');
242 return sb.toString();
246 public String[] getZipDirectoryAndClose(BufferedInputStream bis,
248 String[] s = new String[0];
250 s = getZipDirectoryOrErrorAndClose(bis, manifestID);
252 } catch (Exception e) {
253 System.out.println(e.toString());
258 private String[] getZipDirectoryOrErrorAndClose(BufferedInputStream bis,
261 bis = Rdr.getPngZipStream(bis, true);
262 Lst<String> v = new Lst<String>();
263 ZipInputStream zis = new ZipInputStream(bis);
265 String manifest = null;
266 while ((ze = zis.getNextEntry()) != null) {
267 String fileName = ze.getName();
268 if (manifestID != null && fileName.startsWith(manifestID))
269 manifest = getStreamAsString(zis);
270 else if (!fileName.startsWith("__MACOS")) // resource fork not nec.
274 if (manifestID != null)
275 v.add(0, manifest == null ? "" : manifest + "\n############\n");
276 return v.toArray(new String[v.size()]);
279 public static String getStreamAsString(InputStream is) throws IOException {
280 return Rdr.fixUTF(Rdr.getLimitedStreamBytes(is, -1));
284 public InputStream newGZIPInputStream(InputStream is) throws IOException {
285 return new BufferedInputStream(new GZIPInputStream(is, 512));
289 public BufferedInputStream getUnGzippedInputStream(byte[] bytes) {
291 return Rdr.getUnzippedInputStream(this, Rdr.getBIS(bytes));
292 } catch (Exception e) {
298 public void addZipEntry(Object zos, String fileName) throws IOException {
299 ((ZipOutputStream) zos).putNextEntry(new ZipEntry(fileName));
303 public void closeZipEntry(Object zos) throws IOException {
304 ((ZipOutputStream) zos).closeEntry();
308 public Object getZipOutputStream(Object bos) {
312 * return javajs.api.Interface.getInterface(
313 * "java.util.zip.ZipOutputStream").setZOS(bos);
317 return new ZipOutputStream((OutputStream) bos);
322 public int getCrcValue(byte[] bytes) {
323 CRC32 crc = new CRC32();
324 crc.update(bytes, 0, bytes.length);
325 return (int) crc.getValue();
329 public void readFileAsMap(BufferedInputStream bis, Map<String, Object> bdata, String name) {
330 readFileAsMapStatic(bis, bdata, name);
333 public static void readFileAsMapStatic(BufferedInputStream bis,
334 Map<String, Object> bdata, String name) {
335 int pt = (name == null ? -1 : name.indexOf("|"));
336 name = (pt >= 0 ? name.substring(pt + 1) : null);
338 if (Rdr.isPngZipStream(bis)) {
339 boolean isImage = "_IMAGE_".equals(name);
340 if (name == null || isImage)
341 bdata.put((isImage ? "_DATA_" : "_IMAGE_"), new BArray(getPngImageBytes(bis)));
343 cacheZipContentsStatic(bis, name, bdata, true);
344 } else if (Rdr.isZipS(bis)) {
345 cacheZipContentsStatic(bis, name, bdata, true);
346 } else if (name == null){
347 bdata.put("_DATA_", new BArray(Rdr.getLimitedStreamBytes(bis, -1)));
349 throw new IOException("ZIP file " + name + " not found");
351 bdata.put("$_BINARY_$", Boolean.TRUE);
352 } catch (IOException e) {
354 bdata.put("_ERROR_", e.getMessage());
359 public String cacheZipContents(BufferedInputStream bis,
361 Map<String, Object> cache,
362 boolean asByteArray) {
363 return cacheZipContentsStatic(bis, fileName, cache, asByteArray);
369 * @param fileName may end with "/" for a prefix or contain "|xxxx.xxx" for a specific file or be null
374 public static String cacheZipContentsStatic(BufferedInputStream bis,
375 String fileName, Map<String, Object> cache, boolean asByteArray) {
376 ZipInputStream zis = (ZipInputStream) newZIS(bis);
378 SB listing = new SB();
380 boolean isPath = (fileName != null && fileName.endsWith("/"));
381 boolean oneFile = (asByteArray && !isPath && fileName != null);
382 int pt = (oneFile ? fileName.indexOf("|") : -1);
383 String file0 = (pt >= 0 ? fileName : null);
385 fileName = fileName.substring(0, pt);
386 String prefix = (fileName == null ? "" : isPath ? fileName : fileName + "|");
388 while ((ze = zis.getNextEntry()) != null) {
389 String name = ze.getName();
390 if (fileName != null) {
392 if (!name.equalsIgnoreCase(fileName))
395 listing.append(name).appendC('\n');
398 long nBytes = ze.getSize();
399 byte[] bytes = Rdr.getLimitedStreamBytes(zis, nBytes);
401 readFileAsMapStatic(Rdr.getBIS(bytes), cache, file0);
405 Object o = (asByteArray ? new BArray(bytes) : bytes);
406 cache.put((oneFile ? "_DATA_" : prefix + name), o);
411 } catch (Exception e) {
414 } catch (IOException e1) {
418 if (n == 0 || fileName == null)
420 System.out.println("ZipTools cached " + n + " bytes from " + fileName);
421 return listing.toString();
424 private static byte[] getPngImageBytes(BufferedInputStream bis) {
426 if (Rdr.isPngZipStream(bis)) {
427 int pt_count[] = new int[2];
428 Rdr.getPngZipPointAndCount(bis, pt_count);
429 if (pt_count[1] != 0)
430 return deActivatePngZipB(Rdr.getLimitedStreamBytes(bis, pt_count[0]));
432 return Rdr.getLimitedStreamBytes(bis, -1);
433 } catch (IOException e) {
439 * Once a PNGJ image has been extracted, we want to red-line its
440 * iTXt "Jmol Type PNGJ" tag, since it is no longer associated with
444 * @return disfigured bytes
447 private static byte[] deActivatePngZipB(byte[] bytes) {
448 // \0PNGJ starting at byte 50 changed to \0 NGJ
449 if (Rdr.isPngZipB(bytes))