xref: /AOO41X/main/scripting/java/org/openoffice/idesupport/zip/ParcelZipper.java (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org 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
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 package org.openoffice.idesupport.zip;
29 
30 import java.io.*;
31 import java.util.Enumeration;
32 import java.util.zip.*;
33 import java.beans.PropertyVetoException;
34 
35 import org.openoffice.idesupport.filter.FileFilter;
36 import org.openoffice.idesupport.filter.BinaryOnlyFilter;
37 import org.openoffice.idesupport.filter.ExceptParcelFilter;
38 
39 import com.sun.star.script.framework.container.ParcelDescriptor;
40 import org.openoffice.idesupport.xml.Manifest;
41 
42 public class ParcelZipper
43 {
44     public static final String PARCEL_PREFIX_DIR = "Scripts/";
45     public static final String PARCEL_EXTENSION = "sxp";
46     public static final String CONTENTS_DIRNAME = "Contents";
47     public static final String PARCEL_DESCRIPTOR_XML = "parcel-descriptor.xml";
48 
49     private static ParcelZipper zipper = null;
50 
51     private static final FileFilter DEFAULT_FILTER =
52         BinaryOnlyFilter.getInstance();
53 
54     private ParcelZipper() {
55     }
56 
57     public static ParcelZipper getParcelZipper() {
58         if (zipper == null) {
59             synchronized(ParcelZipper.class) {
60                 if (zipper == null)
61                     zipper = new ParcelZipper();
62             }
63         }
64         return zipper;
65     }
66 
67     public String zipParcel(File basedir) throws IOException {
68         File targetfile, targetdir;
69 
70         if (basedir.getName().equals(CONTENTS_DIRNAME))
71             targetdir = basedir.getParentFile();
72         else
73             targetdir = basedir;
74 
75         targetfile = new File(targetdir, targetdir.getName() + "." + PARCEL_EXTENSION);
76 
77         return zipParcel(basedir, targetfile, DEFAULT_FILTER);
78     }
79 
80     public String zipParcel(File basedir, File targetfile) throws IOException {
81         return zipParcel(basedir, targetfile, DEFAULT_FILTER);
82     }
83 
84     public String zipParcel(File basedir, FileFilter filter) throws IOException {
85         File targetfile, targetdir;
86 
87         if (basedir.getName().equals(CONTENTS_DIRNAME))
88             targetdir = basedir.getParentFile();
89         else
90             targetdir = basedir;
91 
92         targetfile = new File(targetdir, targetdir.getName() + "." + PARCEL_EXTENSION);
93 
94         return zipParcel(basedir, targetfile, filter);
95     }
96 
97     public String zipParcel(File basedir, File targetfile, FileFilter filter)
98         throws IOException {
99         String realpath, tmppath;
100 
101         realpath = targetfile.getPath();
102         tmppath = realpath + ".tmp";
103 
104         File tmpfile = new File(tmppath);
105         ZipOutputStream out = null;
106         try {
107             if (tmpfile.exists() == true)
108                 tmpfile.delete();
109 
110             out = new ZipOutputStream(new FileOutputStream(tmpfile));
111 
112             File[] children = basedir.listFiles();
113             for (int i = 0; i < children.length; i++)
114                 addFileToParcel(children[i], "", out, filter);
115         }
116         catch (IOException ioe) {
117             out.close();
118             tmpfile.delete();
119             throw ioe;
120         }
121         finally {
122             if (out != null)
123                 out.close();
124         }
125 
126         if (targetfile.exists() == true)
127             targetfile.delete();
128         tmpfile.renameTo(targetfile);
129 
130         return targetfile.getAbsolutePath();
131     }
132 
133     private void addFileToParcel(File root, String path, ZipOutputStream out, FileFilter filter)
134         throws IOException {
135         ZipEntry ze;
136 
137         if (root.isDirectory() == true) {
138             ze = new ZipEntry(/* PARCEL_PREFIX_DIR + */ path + root.getName() + "/");
139             out.putNextEntry(ze);
140             out.closeEntry();
141             File[] children = root.listFiles();
142 
143             for (int i = 0; i < children.length; i++)
144                 addFileToParcel(children[i], path + root.getName() + "/", out, filter);
145         }
146         else {
147             if (filter.validate(root.getName()) == false &&
148                 root.getName().equals("parcel-descriptor.xml") == false)
149                 return;
150 
151             ze = new ZipEntry(/* PARCEL_PREFIX_DIR + */ path + root.getName());
152             out.putNextEntry(ze);
153 
154             byte[] bytes = new byte[1024];
155             int len;
156 
157             FileInputStream fis = null;
158             try {
159                 fis = new FileInputStream(root);
160 
161                 while ((len = fis.read(bytes)) != -1)
162                     out.write(bytes, 0, len);
163             }
164             finally {
165                 if (fis != null) fis.close();
166             }
167             out.closeEntry();
168         }
169     }
170 
171     public boolean isOverwriteNeeded(File parcel, File target)
172         throws IOException
173     {
174         boolean result;
175 
176         if (target.isDirectory())
177             result = isDirectoryOverwriteNeeded(parcel, target);
178         else
179             result = isDocumentOverwriteNeeded(parcel, target);
180 
181         return result;
182     }
183 
184     private boolean isDirectoryOverwriteNeeded(File parcel, File target) {
185         String parcelDir = getParcelDirFromParcelZip(parcel.getName());
186 
187         File langdir;
188         try {
189             langdir = new File(target, getParcelLanguage(parcel));
190         }
191         catch (IOException ioe) {
192             return false;
193         }
194 
195         if (langdir.exists() == false)
196             return false;
197 
198         File[] children = langdir.listFiles();
199 
200         for (int i = 0; i < children.length; i++)
201             if (children[i].getName().equals(parcelDir))
202                 return true;
203 
204         return false;
205     }
206 
207     private boolean isDocumentOverwriteNeeded(File parcel, File document)
208         throws IOException
209     {
210         ZipFile documentZip = null;
211         boolean result = false;
212 
213         try {
214             documentZip = new ZipFile(document);
215 
216             String name =
217                 PARCEL_PREFIX_DIR + getParcelLanguage(parcel) +
218                     "/" + getParcelDirFromParcelZip(parcel.getName()) +
219                     "/" + PARCEL_DESCRIPTOR_XML;
220 
221             if (documentZip.getEntry(name) != null)
222                 result = true;
223         }
224         catch (IOException ioe) {
225             return false;
226         }
227         finally {
228             if (documentZip != null) documentZip.close();
229         }
230 
231         return result;
232     }
233 
234     public String deployParcel(File parcel, File target)
235         throws IOException {
236 
237         String output = null;
238         if (target.isDirectory())
239             output = unzipToDirectory(parcel, target);
240         else
241             output = unzipToZip(parcel, target);
242         return output;
243     }
244 
245     private String getParcelDirFromParcelZip(String zipname) {
246         String result = zipname.substring(0, zipname.lastIndexOf("."));
247         return result;
248     }
249 
250     private String unzipToDirectory(File parcel, File targetDirectory)
251         throws IOException {
252 
253         ZipInputStream in = null;
254         File parcelDir = new File(targetDirectory,
255             getParcelLanguage(parcel) + File.separator +
256             getParcelDirFromParcelZip(parcel.getName()));
257 
258         if (isDirectoryOverwriteNeeded(parcel, targetDirectory)) {
259             if (deleteDir(parcelDir) == false) {
260                 throw new IOException("Could not overwrite: " +
261                     parcelDir.getAbsolutePath());
262             }
263         }
264 
265         try {
266             in = new ZipInputStream(new FileInputStream(parcel));
267 
268             File outFile;
269             ZipEntry inEntry = in.getNextEntry();
270             byte[] bytes = new byte[1024];
271             int len;
272 
273             while (inEntry != null) {
274                 outFile = new File(parcelDir, inEntry.getName());
275                 if (inEntry.isDirectory()) {
276                     outFile.mkdir();
277                 }
278                 else {
279                     if (outFile.getParentFile().exists() != true)
280                         outFile.getParentFile().mkdirs();
281 
282                     FileOutputStream out = null;
283                     try {
284                         out = new FileOutputStream(outFile);
285 
286                         while ((len = in.read(bytes)) != -1)
287                             out.write(bytes, 0, len);
288                     }
289                     finally {
290                         if (out != null) out.close();
291                     }
292                 }
293                 inEntry = in.getNextEntry();
294             }
295         }
296         finally {
297             if (in != null) in.close();
298         }
299 
300         return parcelDir.getAbsolutePath();
301     }
302 
303     private boolean deleteDir(File basedir) {
304         if (basedir.isDirectory()) {
305             String[] children = basedir.list();
306             for (int i=0; i<children.length; i++) {
307                 boolean success = deleteDir(new File(basedir, children[i]));
308                 if (!success) {
309                     return false;
310                 }
311             }
312         }
313         return basedir.delete();
314     }
315 
316     private String unzipToZip(File parcel, File targetDocument)
317         throws IOException {
318 
319         ZipInputStream documentStream = null;
320         ZipInputStream parcelStream = null;
321         ZipOutputStream outStream = null;
322         Manifest manifest;
323 
324         String language = getParcelLanguage(parcel);
325 
326         if (isDocumentOverwriteNeeded(parcel, targetDocument)) {
327             String parcelName = language + "/" +
328                 getParcelDirFromParcelZip(parcel.getName());
329             removeParcel(targetDocument, parcelName);
330         }
331 
332         // first write contents of document to tmpfile
333         File tmpfile = new File(targetDocument.getAbsolutePath() + ".tmp");
334 
335         manifest = addParcelToManifest(targetDocument, parcel);
336 
337         try {
338             documentStream =
339                 new ZipInputStream(new FileInputStream(targetDocument));
340             parcelStream = new ZipInputStream(new FileInputStream(parcel));
341             outStream = new ZipOutputStream(new FileOutputStream(tmpfile));
342 
343             copyParcelToZip(parcelStream, outStream, PARCEL_PREFIX_DIR +
344                 language + "/" + getParcelDirFromParcelZip(parcel.getName()));
345             copyDocumentToZip(documentStream, outStream, manifest);
346         }
347         catch (IOException ioe) {
348             tmpfile.delete();
349             throw ioe;
350         }
351         finally {
352             if (documentStream != null) documentStream.close();
353             if (parcelStream != null) parcelStream.close();
354             if (outStream != null) outStream.close();
355         }
356 
357         if (targetDocument.delete() == false) {
358             tmpfile.delete();
359             throw new IOException("Could not overwrite " + targetDocument);
360         }
361         else
362             tmpfile.renameTo(targetDocument);
363 
364         return targetDocument.getAbsolutePath();
365     }
366 
367     private void copyParcelToZip(ZipInputStream in, ZipOutputStream out,
368         String parcelName) throws IOException {
369 
370         ZipEntry outEntry;
371         ZipEntry inEntry = in.getNextEntry();
372         byte[] bytes = new byte[1024];
373         int len;
374 
375         while (inEntry != null) {
376             if(parcelName!=null)
377                 outEntry = new ZipEntry(parcelName + "/" + inEntry.getName());
378             else
379                 outEntry = new ZipEntry(inEntry);
380             out.putNextEntry(outEntry);
381 
382             if (inEntry.isDirectory() == false)
383                 while ((len = in.read(bytes)) != -1)
384                     out.write(bytes, 0, len);
385 
386             out.closeEntry();
387             inEntry = in.getNextEntry();
388         }
389     }
390 
391     private void copyDocumentToZip(ZipInputStream in, ZipOutputStream out,
392         Manifest manifest) throws IOException {
393 
394         ZipEntry outEntry;
395         ZipEntry inEntry;
396         byte[] bytes = new byte[1024];
397         int len;
398 
399         while ((inEntry = in.getNextEntry()) != null) {
400 
401             outEntry = new ZipEntry(inEntry);
402             out.putNextEntry(outEntry);
403 
404             if(inEntry.getName().equals("META-INF/manifest.xml") &&
405                manifest != null) {
406                 InputStream manifestStream = null;
407                 try {
408                     manifestStream = manifest.getInputStream();
409                     while ((len = manifestStream.read(bytes)) != -1)
410                         out.write(bytes, 0, len);
411                 }
412                 finally {
413                     if (manifestStream != null)
414                         manifestStream.close();
415                 }
416             }
417             else if (inEntry.isDirectory() == false) {
418                 while ((len = in.read(bytes)) != -1)
419                     out.write(bytes, 0, len);
420             }
421 
422             out.closeEntry();
423         }
424     }
425 
426     public String removeParcel(File document, String parcelName)
427         throws IOException {
428 
429         ZipInputStream documentStream = null;
430         ZipOutputStream outStream = null;
431         Manifest manifest = null;
432 
433         if (!parcelName.startsWith(PARCEL_PREFIX_DIR))
434             parcelName = PARCEL_PREFIX_DIR + parcelName;
435         manifest = removeParcelFromManifest(document, parcelName);
436 
437         // first write contents of document to tmpfile
438         File tmpfile = new File(document.getAbsolutePath() + ".tmp");
439 
440         try {
441             ZipEntry outEntry;
442             ZipEntry inEntry;
443             byte[] bytes = new byte[1024];
444             int len;
445 
446             documentStream = new ZipInputStream(new FileInputStream(document));
447             outStream = new ZipOutputStream(new FileOutputStream(tmpfile));
448 
449             while ((inEntry = documentStream.getNextEntry()) != null) {
450 
451                 if(inEntry.getName().startsWith(parcelName))
452                     continue;
453 
454                 outEntry = new ZipEntry(inEntry);
455                 outStream.putNextEntry(outEntry);
456 
457                 if(inEntry.getName().equals("META-INF/manifest.xml") &&
458                    manifest != null) {
459                     InputStream manifestStream = null;
460                     try {
461                         manifestStream = manifest.getInputStream();
462                         while ((len = manifestStream.read(bytes)) != -1)
463                             outStream.write(bytes, 0, len);
464                     }
465                     finally {
466                         if (manifestStream != null)
467                             manifestStream.close();
468                     }
469                 }
470                 else if (inEntry.isDirectory() == false) {
471                     while ((len = documentStream.read(bytes)) != -1)
472                         outStream.write(bytes, 0, len);
473                 }
474 
475                 outStream.closeEntry();
476             }
477         }
478         catch (IOException ioe) {
479             tmpfile.delete();
480             throw ioe;
481         }
482         finally {
483             if (documentStream != null)
484                 documentStream.close();
485 
486             if (outStream != null)
487                 outStream.close();
488         }
489 
490         if (document.delete() == false) {
491             tmpfile.delete();
492             throw new IOException("Could not overwrite " + document);
493         }
494         else
495             tmpfile.renameTo(document);
496 
497         return document.getAbsolutePath();
498     }
499 
500     private Manifest getManifestFromDocument(File document) {
501         ZipFile documentZip = null;
502         Manifest result = null;
503 
504         try {
505             documentZip = new ZipFile(document);
506             ZipEntry original = documentZip.getEntry("META-INF/manifest.xml");
507             if (original != null) {
508                 result = new Manifest(documentZip.getInputStream(original));
509             }
510         }
511         catch (IOException ioe) {
512             result = null;
513         }
514         finally {
515             try {
516                 if (documentZip != null)
517                     documentZip.close();
518             }
519             catch (IOException ioe) {}
520         }
521 
522         return result;
523     }
524 
525     private Manifest addParcelToManifest(File document, File parcel)
526         throws IOException {
527 
528         ZipFile parcelZip = null;
529         Manifest result = null;
530 
531         result = getManifestFromDocument(document);
532         if (result == null)
533             return null;
534 
535         String language = getParcelLanguage(parcel);
536 
537         try {
538             parcelZip = new ZipFile(parcel);
539 
540             String prefix = PARCEL_PREFIX_DIR + language + "/" +
541                 getParcelDirFromParcelZip(parcel.getName()) + "/";
542 
543             Enumeration entries = parcelZip.entries();
544             while (entries.hasMoreElements()) {
545                 ZipEntry entry = (ZipEntry)entries.nextElement();
546                 result.add(prefix + entry.getName());
547             }
548         }
549         catch (IOException ioe) {
550             return null;
551         }
552         finally {
553             try {
554                 if (parcelZip != null)
555                     parcelZip.close();
556             }
557             catch (IOException ioe) {}
558         }
559 
560         return result;
561     }
562 
563     private Manifest removeParcelFromManifest(File document, String name) {
564         Manifest result = null;
565 
566         result = getManifestFromDocument(document);
567         if (result == null)
568             return null;
569 
570         result.remove(name);
571         return result;
572     }
573 
574     public String getParcelLanguage(File file) throws IOException {
575         ZipFile zf = null;
576         ZipEntry ze = null;
577         InputStream is = null;
578         ParcelDescriptor pd;
579 
580         try {
581             zf = new ZipFile(file);
582             ze = zf.getEntry(PARCEL_DESCRIPTOR_XML);
583 
584             if (ze == null)
585                 throw new IOException("Could not find Parcel Descriptor in parcel");
586 
587             is = zf.getInputStream(ze);
588             pd = new ParcelDescriptor(is);
589         }
590         finally {
591             if (zf != null)
592                 zf.close();
593 
594             if (is != null)
595                 is.close();
596         }
597 
598         return pd.getLanguage().toLowerCase();
599     }
600 }
601