Functionality:
    The Zip task now allows users to specify that they wish to update an
existing file rather than creating a new one.

Configuration:
    The "update" property is a simple boolean, defaulting to false.

Implementation:
    If update is requested (and the zip file currently exists) we rename
it to a temporary file, add the specified files in the normal way,
keeping track of what we added, then add the contents of the temporary
file back in, excluding any files added.

Platform:
    This should work with JDK1.1 and higher.

Caveats:
    We keep track of added files in zipFile(InputStream in,
ZipOutputStream zOut, String vPath, long lastModified). If subclasses
override this (or add files in a different way, or override execute and
don't call the original), the functionality will either be lost or
broken. (Lost if they override execute), broken if they don't call
zipFile.) Further, the list of added files is kept as an instance
variable - if a single instance is used in two threads at the same time,
there could be problems. However, there would already be problems due to
addedDirs not being thread-safe either. (It wasn't cleared on cleanup,
either, which I've fixed.) Oh, and a general caveat that this is only my
second change to Ant, so could do with a lot of testing and
code-checking just in case I've misunderstood something and/or been
stupid.

Documentation:
    Haven't updated anything yet; zip.html should be changed - what
about subclasses such as Jar?

Diff: (using diff -uw Zip.java.original Zip.java, please tell me if
that's not correct)
    (See attached file - I didn't want to risk Exchange wrapping it...)

Please let me know if you try this out - both good and bad experiences
are welcome :)

Jon
--- Zip.java.original   Mon Jun 11 13:06:27 2001
+++ Zip.java    Mon Jun 11 13:09:47 2001
@@ -78,12 +78,14 @@
     private File zipFile;
     private File baseDir;
     private boolean doCompress = true;
+    private boolean doUpdate = false;
     protected String archiveType = "zip";
     // For directories:
     private static long emptyCrc = new CRC32 ().getValue ();
     protected String emptyBehavior = "skip";
     private Vector filesets = new Vector ();
     private Hashtable addedDirs = new Hashtable();
+    private Vector addedFiles = new Vector();
 
     /**
      * This is the name/location of where to 
@@ -109,6 +111,14 @@
     }
 
     /**
+     * Sets whether we want to update the file (if it exists)
+     * or create a new one.
+     */
+    public void setUpdate(boolean c) {
+        doUpdate = c;
+    }
+
+    /**
      * Adds a set of files (nested fileset attribute).
      */
     public void addFileset(FileSet set) {
@@ -154,6 +164,39 @@
             throw new BuildException("You must specify the " + archiveType + " 
file to create!");
         }
 
+        // Renamed version of original file, if it exists
+        File renamedFile=null;
+        // Whether or not an actual update is required -
+        // we don't need to update if the original file doesn't exist
+        boolean reallyDoUpdate=false;
+        if (doUpdate && zipFile.exists())
+        {
+            reallyDoUpdate=true;
+            
+            int i;
+            for (i=0; i < 1000; i++)
+            {
+                renamedFile = new File (zipFile.getParent(), "tmp."+i);
+                
+                if (!renamedFile.exists())
+                    break;
+            }
+            if (i==1000)
+                throw new BuildException 
+                ("Can't find temporary filename to rename old file to.");
+            try
+            {
+                if (!zipFile.renameTo (renamedFile))
+                    throw new BuildException 
+                    ("Unable to rename old file to temporary file");
+            }
+            catch (SecurityException e)
+            {
+                throw new BuildException 
+                    ("Not allowed to rename old file to temporary file");
+            }
+        }
+        
         // Create the scanners to pass to isUpToDate().
         Vector dss = new Vector ();
         if (baseDir != null)
@@ -170,10 +213,12 @@
         // can also handle empty archives
         if (isUpToDate(scanners, zipFile)) return;
 
-        log("Building "+ archiveType +": "+ zipFile.getAbsolutePath());
+        String action=reallyDoUpdate ? "Updating " : "Building ";
+        
+        log(action + archiveType +": "+ zipFile.getAbsolutePath());
 
-        try {
             boolean success = false;
+        try {
             ZipOutputStream zOut = 
               new ZipOutputStream(new FileOutputStream(zipFile));
             try {
@@ -189,6 +234,24 @@
                     addFiles(getDirectoryScanner(baseDir), zOut, "", "");
                 // Add the explicit filesets to the archive.
                 addFiles(filesets, zOut);
+                if (reallyDoUpdate)
+                {
+                    ZipFileSet oldFiles = new ZipFileSet ();
+                    oldFiles.setSrc (renamedFile);
+                    
+                    StringBuffer exclusionPattern=new StringBuffer();
+                    for (int i=0; i < addedFiles.size(); i++)
+                    {
+                        if (i != 0)
+                            exclusionPattern.append (",");
+                        exclusionPattern.append 
+                            ((String) addedFiles.elementAt(i));
+                    }
+                    oldFiles.setExcludes (exclusionPattern.toString());
+                    Vector tmp = new Vector();
+                    tmp.addElement (oldFiles);
+                    addFiles (tmp, zOut);
+                }
                 success = true;
             } finally {
                 // Close the output stream.
@@ -214,10 +277,22 @@
                 msg += " (and the archive is probably corrupt but I could not 
delete it)";
             }
 
+            if (reallyDoUpdate) {
+                if (!renamedFile.renameTo (zipFile)) {
+                    msg+=" (and I couldn't rename the temporary file "+
+                        renamedFile.getName()+" back)";
+                }
+            }
+            
             throw new BuildException(msg, ioe, location);
         } finally {
             cleanUp();
         }
+        // If we've been successful on an update, delete the temporary file
+        if (success && reallyDoUpdate)
+            if (!renamedFile.delete())
+                log ("Warning: unable to delete temporary file "+
+                     renamedFile.getName(), Project.MSG_WARN);
     }
 
     /**
@@ -488,6 +563,7 @@
             }
             count = in.read(buffer, 0, buffer.length);
         } while (count != -1);
+        addedFiles.addElement (vPath);
     }
 
     protected void zipFile(File file, ZipOutputStream zOut, String vPath)
@@ -585,5 +661,8 @@
      * <p>When we get here, the Zip file has been closed and all we
      * need to do is to reset some globals.</p>
      */
-    protected void cleanUp() {}
+    protected void cleanUp() {
+        addedDirs = new Hashtable();
+        addedFiles = new Vector();
+    }
 }

Reply via email to