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