6 * Copyright (C) 2006 The Jmol Development Team
8 * Contact: jmol-developers@lists.sf.net
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 import java.io.BufferedInputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.OutputStream;
33 import javajs.J2SIgnoreImport;
34 import javajs.api.GenericZipInputStream;
35 import javajs.api.GenericZipTools;
36 import javajs.api.ZInputStream;
39 import java.util.zip.CRC32;
40 import java.util.zip.GZIPInputStream;
41 import java.util.zip.ZipEntry;
42 import java.util.zip.ZipInputStream;
43 import java.util.zip.ZipOutputStream;
47 * Note the JSmol/HTML5 must use its own version of java.util.zip.ZipOutputStream.
50 @J2SIgnoreImport({ java.util.zip.ZipOutputStream.class })
51 public class ZipTools implements GenericZipTools {
58 public ZInputStream newZipInputStream(InputStream is) {
62 @SuppressWarnings("resource")
63 private static ZInputStream newZIS(InputStream is) {
64 return (is instanceof ZInputStream ? (ZInputStream) is
65 : is instanceof BufferedInputStream ? new GenericZipInputStream(is)
66 : new GenericZipInputStream(new BufferedInputStream(is)));
70 * reads a ZIP file and saves all data in a Hashtable so that the files may be
71 * organized later in a different order. Also adds a #Directory_Listing entry.
73 * Files are bracketed by BEGIN Directory Entry and END Directory Entry lines,
74 * similar to CompoundDocument.getAllData.
79 * prefix for entry listing
80 * @param binaryFileList
81 * |-separated list of files that should be saved as xx xx xx hex byte
82 * strings. The directory listing is appended with ":asBinaryString"
86 public void getAllZipData(InputStream is, String[] subfileList,
87 String name0, String binaryFileList,
88 Map<String, String> fileData) {
89 ZipInputStream zis = (ZipInputStream) newZIS(is);
91 SB listing = new SB();
92 binaryFileList = "|" + binaryFileList + "|";
93 String prefix = PT.join(subfileList, '/', 1);
94 String prefixd = null;
96 prefixd = prefix.substring(0, prefix.indexOf("/") + 1);
97 if (prefixd.length() == 0)
101 while ((ze = zis.getNextEntry()) != null) {
102 String name = ze.getName();
103 if (prefix != null && prefixd != null
104 && !(name.equals(prefix) || name.startsWith(prefixd)))
106 //System.out.println("ziputil: " + name);
107 listing.append(name).appendC('\n');
108 String sname = "|" + name.substring(name.lastIndexOf("/") + 1) + "|";
109 boolean asBinaryString = (binaryFileList.indexOf(sname) >= 0);
110 byte[] bytes = Rdr.getLimitedStreamBytes(zis, ze.getSize());
112 if (asBinaryString) {
113 str = getBinaryStringForBytes(bytes);
114 name += ":asBinaryString";
116 str = Rdr.fixUTF(bytes);
118 str = "BEGIN Directory Entry " + name + "\n" + str
119 + "\nEND Directory Entry " + name + "\n";
120 fileData.put(name0 + "|" + name, str);
122 } catch (Exception e) {
124 fileData.put("#Directory_Listing", listing.toString());
127 private String getBinaryStringForBytes(byte[] bytes) {
129 for (int i = 0; i < bytes.length; i++)
130 ret.append(Integer.toHexString(bytes[i] & 0xFF)).appendC(' ');
131 return ret.toString();
135 * iteratively drills into zip files of zip files to extract file content or
136 * zip file directory. Also works with JAR files.
138 * Does not return "__MACOS" paths
143 * @param asBufferedInputStream
145 * @return directory listing or subfile contents
148 public Object getZipFileDirectory(BufferedInputStream bis, String[] list,
149 int listPtr, boolean asBufferedInputStream) {
151 if (list == null || listPtr >= list.length)
152 return getZipDirectoryAsStringAndClose(bis);
153 bis = Rdr.getPngZipStream(bis, true);
154 String fileName = list[listPtr];
155 ZipInputStream zis = new ZipInputStream(bis);
157 //System.out.println("fname=" + fileName);
159 boolean isAll = (fileName.equals("."));
160 if (isAll || fileName.lastIndexOf("/") == fileName.length() - 1) {
162 while ((ze = zis.getNextEntry()) != null) {
163 String name = ze.getName();
164 if (isAll || name.startsWith(fileName))
165 ret.append(name).appendC('\n');
167 String str = ret.toString();
168 return (asBufferedInputStream ? Rdr.getBIS(str.getBytes()) : str);
170 int pt = fileName.indexOf(":asBinaryString");
171 boolean asBinaryString = (pt > 0);
173 fileName = fileName.substring(0, pt);
174 fileName = fileName.replace('\\', '/');
175 while ((ze = zis.getNextEntry()) != null
176 && !fileName.equals(ze.getName())) {
178 byte[] bytes = (ze == null ? null : Rdr.getLimitedStreamBytes(zis,
184 if (Rdr.isZipB(bytes) || Rdr.isPngZipB(bytes))
185 return getZipFileDirectory(Rdr.getBIS(bytes), list, ++listPtr,
186 asBufferedInputStream);
187 if (asBufferedInputStream)
188 return Rdr.getBIS(bytes);
189 if (asBinaryString) {
191 for (int i = 0; i < bytes.length; i++)
192 ret.append(Integer.toHexString(bytes[i] & 0xFF)).appendC(' ');
193 return ret.toString();
195 if (Rdr.isGzipB(bytes))
196 bytes = Rdr.getLimitedStreamBytes(getUnGzippedInputStream(bytes), -1);
197 return Rdr.fixUTF(bytes);
198 } catch (Exception e) {
204 public byte[] getZipFileContentsAsBytes(BufferedInputStream bis,
205 String[] list, int listPtr) {
206 byte[] ret = new byte[0];
207 String fileName = list[listPtr];
208 if (fileName.lastIndexOf("/") == fileName.length() - 1)
211 bis = Rdr.getPngZipStream(bis, true);
212 ZipInputStream zis = new ZipInputStream(bis);
214 while ((ze = zis.getNextEntry()) != null) {
215 if (!fileName.equals(ze.getName()))
217 byte[] bytes = Rdr.getLimitedStreamBytes(zis, ze.getSize());
218 return ((Rdr.isZipB(bytes) || Rdr.isPngZipB(bytes)) && ++listPtr < list.length ? getZipFileContentsAsBytes(
219 Rdr.getBIS(bytes), list, listPtr) : bytes);
221 } catch (Exception e) {
227 public String getZipDirectoryAsStringAndClose(BufferedInputStream bis) {
229 String[] s = new String[0];
231 s = getZipDirectoryOrErrorAndClose(bis, null);
233 } catch (Exception e) {
234 System.out.println(e.toString());
236 for (int i = 0; i < s.length; i++)
237 sb.append(s[i]).appendC('\n');
238 return sb.toString();
242 public String[] getZipDirectoryAndClose(BufferedInputStream bis,
244 String[] s = new String[0];
246 s = getZipDirectoryOrErrorAndClose(bis, manifestID);
248 } catch (Exception e) {
249 System.out.println(e.toString());
254 private String[] getZipDirectoryOrErrorAndClose(BufferedInputStream bis,
257 bis = Rdr.getPngZipStream(bis, true);
258 Lst<String> v = new Lst<String>();
259 ZipInputStream zis = new ZipInputStream(bis);
261 String manifest = null;
262 while ((ze = zis.getNextEntry()) != null) {
263 String fileName = ze.getName();
264 if (manifestID != null && fileName.startsWith(manifestID))
265 manifest = getStreamAsString(zis);
266 else if (!fileName.startsWith("__MACOS")) // resource fork not nec.
270 if (manifestID != null)
271 v.add(0, manifest == null ? "" : manifest + "\n############\n");
272 return v.toArray(new String[v.size()]);
275 public static String getStreamAsString(InputStream is) throws IOException {
276 return Rdr.fixUTF(Rdr.getLimitedStreamBytes(is, -1));
280 public InputStream newGZIPInputStream(InputStream is) throws IOException {
281 return new BufferedInputStream(new GZIPInputStream(is, 512));
285 public BufferedInputStream getUnGzippedInputStream(byte[] bytes) {
287 return Rdr.getUnzippedInputStream(this, Rdr.getBIS(bytes));
288 } catch (Exception e) {
294 public void addZipEntry(Object zos, String fileName) throws IOException {
295 ((ZipOutputStream) zos).putNextEntry(new ZipEntry(fileName));
299 public void closeZipEntry(Object zos) throws IOException {
300 ((ZipOutputStream) zos).closeEntry();
304 public Object getZipOutputStream(Object bos) {
308 * return javajs.api.Interface.getInterface(
309 * "java.util.zip.ZipOutputStream").setZOS(bos);
313 return new ZipOutputStream((OutputStream) bos);
318 public int getCrcValue(byte[] bytes) {
319 CRC32 crc = new CRC32();
320 crc.update(bytes, 0, bytes.length);
321 return (int) crc.getValue();
325 public void readFileAsMap(BufferedInputStream bis, Map<String, Object> bdata, String name) {
326 int pt = (name == null ? -1 : name.indexOf("|"));
327 name = (pt >= 0 ? name.substring(pt + 1) : null);
329 if (Rdr.isPngZipStream(bis)) {
330 boolean isImage = "_IMAGE_".equals(name);
331 if (name == null || isImage)
332 bdata.put((isImage ? "_DATA_" : "_IMAGE_"), new BArray(getPngImageBytes(bis)));
334 cacheZipContents(bis, name, bdata, true);
335 } else if (Rdr.isZipS(bis)) {
336 cacheZipContents(bis, name, bdata, true);
337 } else if (name == null){
338 bdata.put("_DATA_", new BArray(Rdr.getLimitedStreamBytes(bis, -1)));
340 throw new IOException("ZIP file " + name + " not found");
342 bdata.put("$_BINARY_$", Boolean.TRUE);
343 } catch (IOException e) {
345 bdata.put("_ERROR_", e.getMessage());
350 public String cacheZipContents(BufferedInputStream bis,
352 Map<String, Object> cache,
353 boolean asByteArray) {
354 ZipInputStream zis = (ZipInputStream) newZIS(bis);
356 SB listing = new SB();
358 boolean oneFile = (asByteArray && fileName != null);
359 int pt = (oneFile ? fileName.indexOf("|") : -1);
360 String file0 = (pt >= 0 ? fileName : null);
362 fileName = fileName.substring(0, pt);
364 while ((ze = zis.getNextEntry()) != null) {
365 String name = ze.getName();
366 if (fileName != null) {
368 if (!name.equalsIgnoreCase(fileName))
371 listing.append(name).appendC('\n');
374 long nBytes = ze.getSize();
375 byte[] bytes = Rdr.getLimitedStreamBytes(zis, nBytes);
377 readFileAsMap(Rdr.getBIS(bytes), cache, file0);
381 Object o = (asByteArray ? new BArray(bytes) : bytes);
382 cache.put((oneFile ? "_DATA_" : (fileName == null ? "" : fileName + "|") + name), o);
387 } catch (Exception e) {
390 } catch (IOException e1) {
394 if (n == 0 || fileName == null)
396 System.out.println("ZipTools cached " + n + " bytes from " + fileName);
397 return listing.toString();
400 private static byte[] getPngImageBytes(BufferedInputStream bis) {
402 if (Rdr.isPngZipStream(bis)) {
403 int pt_count[] = new int[2];
404 Rdr.getPngZipPointAndCount(bis, pt_count);
405 if (pt_count[1] != 0)
406 return deActivatePngZipB(Rdr.getLimitedStreamBytes(bis, pt_count[0]));
408 return Rdr.getLimitedStreamBytes(bis, -1);
409 } catch (IOException e) {
415 * Once a PNGJ image has been extracted, we want to red-line its
416 * iTXt "Jmol Type PNGJ" tag, since it is no longer associated with
420 * @return disfigured bytes
423 private static byte[] deActivatePngZipB(byte[] bytes) {
424 // \0PNGJ starting at byte 50 changed to \0 NGJ
425 if (Rdr.isPngZipB(bytes))