FileDocCategorySizeDatePackage
Zip.javaAPI DocApache Ant 1.7061577Wed Dec 13 06:16:18 GMT 2006org.apache.tools.ant.taskdefs

Zip

public class Zip extends MatchingTask
Create a Zip file.
since
Ant 1.1
ant.task
category="packaging"

Fields Summary
protected File
zipFile
private org.apache.tools.ant.types.ZipScanner
zs
private File
baseDir
protected Hashtable
entries
private Vector
groupfilesets
private Vector
filesetsFromGroupfilesets
protected String
duplicate
private boolean
doCompress
private boolean
doUpdate
private boolean
savedDoUpdate
private boolean
doFilesonly
protected String
archiveType
private static final long
EMPTY_CRC
protected String
emptyBehavior
private Vector
resources
protected Hashtable
addedDirs
private Vector
addedFiles
protected boolean
doubleFilePass
protected boolean
skipWriting
private static final org.apache.tools.ant.util.FileUtils
FILE_UTILS
private boolean
addingNewFiles
true when we are adding new files into the Zip file, as opposed to adding back the unchanged files
private String
encoding
Encoding to use for filenames, defaults to the platform's default encoding.
private boolean
keepCompression
Whether the original compression of entries coming from a ZIP archive should be kept (for example when updating an archive).
private boolean
roundUp
Whether the file modification times will be rounded up to the next even number of seconds.
private String
comment
Comment for the archive.
private int
level
Constructors Summary
Methods Summary
public voidadd(org.apache.tools.ant.types.ResourceCollection a)
Add a collection of resources to be archived.

param
a the resources to archive
since
Ant 1.7

        resources.add(a);
    
public voidaddFileset(org.apache.tools.ant.types.FileSet set)
Adds a set of files.

param
set the fileset to add

        add(set);
    
protected final voidaddParentDirs(java.io.File baseDir, java.lang.String entry, org.apache.tools.zip.ZipOutputStream zOut, java.lang.String prefix, int dirMode)
Ensure all parent dirs of a given entry have been added.

param
baseDir the base directory to use (may be null)
param
entry the entry name to create directories from
param
zOut the stream to write to
param
prefix a prefix to place on the created entries
param
dirMode the directory mode
throws
IOException on error
since
Ant 1.5.2

        if (!doFilesonly) {
            Stack directories = new Stack();
            int slashPos = entry.length();

            while ((slashPos = entry.lastIndexOf('/", slashPos - 1)) != -1) {
                String dir = entry.substring(0, slashPos + 1);
                if (addedDirs.get(prefix + dir) != null) {
                    break;
                }
                directories.push(dir);
            }

            while (!directories.isEmpty()) {
                String dir = (String) directories.pop();
                File f = null;
                if (baseDir != null) {
                    f = new File(baseDir, dir);
                } else {
                    f = new File(dir);
                }
                zipDir(f, zOut, prefix + dir, dirMode);
            }
        }
    
protected final voidaddResources(org.apache.tools.ant.types.FileSet fileset, org.apache.tools.ant.types.Resource[] resources, org.apache.tools.zip.ZipOutputStream zOut)
Add the given resources.

param
fileset may give additional information like fullpath or permissions.
param
resources the resources to add
param
zOut the stream to write to
throws
IOException on error
since
Ant 1.5.2


        String prefix = "";
        String fullpath = "";
        int dirMode = ArchiveFileSet.DEFAULT_DIR_MODE;
        int fileMode = ArchiveFileSet.DEFAULT_FILE_MODE;

        ArchiveFileSet zfs = null;
        if (fileset instanceof ArchiveFileSet) {
            zfs = (ArchiveFileSet) fileset;
            prefix = zfs.getPrefix(getProject());
            fullpath = zfs.getFullpath(getProject());
            dirMode = zfs.getDirMode(getProject());
            fileMode = zfs.getFileMode(getProject());
        }

        if (prefix.length() > 0 && fullpath.length() > 0) {
            throw new BuildException("Both prefix and fullpath attributes must"
                                     + " not be set on the same fileset.");
        }

        if (resources.length != 1 && fullpath.length() > 0) {
            throw new BuildException("fullpath attribute may only be specified"
                                     + " for filesets that specify a single"
                                     + " file.");
        }

        if (prefix.length() > 0) {
            if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
                prefix += "/";
            }
            addParentDirs(null, prefix, zOut, "", dirMode);
        }

        ZipFile zf = null;
        try {
            boolean dealingWithFiles = false;
            File base = null;

            if (zfs == null || zfs.getSrc(getProject()) == null) {
                dealingWithFiles = true;
                base = fileset.getDir(getProject());
            } else if (zfs instanceof ZipFileSet) {
                zf = new ZipFile(zfs.getSrc(getProject()), encoding);
            }

            for (int i = 0; i < resources.length; i++) {
                String name = null;
                if (fullpath.length() > 0) {
                    name = fullpath;
                } else {
                    name = resources[i].getName();
                }
                name = name.replace(File.separatorChar, '/");

                if ("".equals(name)) {
                    continue;
                }
                if (resources[i].isDirectory() && !name.endsWith("/")) {
                    name = name + "/";
                }

                if (!doFilesonly && !dealingWithFiles
                    && resources[i].isDirectory()
                    && !zfs.hasDirModeBeenSet()) {
                    int nextToLastSlash = name.lastIndexOf("/",
                                                           name.length() - 2);
                    if (nextToLastSlash != -1) {
                        addParentDirs(base, name.substring(0,
                                                           nextToLastSlash + 1),
                                      zOut, prefix, dirMode);
                    }
                    if (zf != null) {
                        ZipEntry ze = zf.getEntry(resources[i].getName());
                        addParentDirs(base, name, zOut, prefix,
                                      ze.getUnixMode());
                    } else {
                        ArchiveResource tr = (ArchiveResource) resources[i];
                        addParentDirs(base, name, zOut, prefix,
                                      tr.getMode());
                    }

                } else {
                    addParentDirs(base, name, zOut, prefix, dirMode);
                }

                if (!resources[i].isDirectory() && dealingWithFiles) {
                    File f = FILE_UTILS.resolveFile(base,
                                                   resources[i].getName());
                    zipFile(f, zOut, prefix + name, fileMode);
                } else if (!resources[i].isDirectory()) {
                    if (zf != null) {
                    ZipEntry ze = zf.getEntry(resources[i].getName());

                    if (ze != null) {
                        boolean oldCompress = doCompress;
                        if (keepCompression) {
                            doCompress = (ze.getMethod() == ZipEntry.DEFLATED);
                        }
                        try {
                            zipFile(zf.getInputStream(ze), zOut, prefix + name,
                                    ze.getTime(), zfs.getSrc(getProject()),
                                    zfs.hasFileModeBeenSet() ? fileMode
                                    : ze.getUnixMode());
                        } finally {
                            doCompress = oldCompress;
                        }
                    }
                    } else {
                        ArchiveResource tr = (ArchiveResource) resources[i];
                        InputStream is = null;
                        try {
                            is = tr.getInputStream();
                            zipFile(is, zOut, prefix + name,
                                    resources[i].getLastModified(),
                                    zfs.getSrc(getProject()),
                                    zfs.hasFileModeBeenSet() ? fileMode
                                    : tr.getMode());
                        } finally {
                            FileUtils.close(is);
                        }
                    }
                }
            }
        } finally {
            if (zf != null) {
                zf.close();
            }
        }
    
protected final voidaddResources(org.apache.tools.ant.types.ResourceCollection rc, org.apache.tools.ant.types.Resource[] resources, org.apache.tools.zip.ZipOutputStream zOut)
Add the given resources.

param
rc may give additional information like fullpath or permissions.
param
resources the resources to add
param
zOut the stream to write to
throws
IOException on error
since
Ant 1.7

        if (rc instanceof FileSet) {
            addResources((FileSet) rc, resources, zOut);
            return;
        }
        for (int i = 0; i < resources.length; i++) {
            String name = resources[i].getName().replace(File.separatorChar,
                                                         '/");
            if ("".equals(name)) {
                continue;
            }
            if (resources[i].isDirectory() && doFilesonly) {
                continue;
            }
            File base = null;
            if (resources[i] instanceof FileResource) {
                base = ((FileResource) resources[i]).getBaseDir();
            }
            if (resources[i].isDirectory()) {
                if (!name.endsWith("/")) {
                    name = name + "/";
                }
            }

            addParentDirs(base, name, zOut, "",
                          ArchiveFileSet.DEFAULT_DIR_MODE);

            if (!resources[i].isDirectory()) {
                if (resources[i] instanceof FileResource) {
                    File f = ((FileResource) resources[i]).getFile();
                    zipFile(f, zOut, name, ArchiveFileSet.DEFAULT_FILE_MODE);
                } else {
                    InputStream is = null;
                    try {
                        is = resources[i].getInputStream();
                        zipFile(is, zOut, name,
                                resources[i].getLastModified(),
                                null, ArchiveFileSet.DEFAULT_FILE_MODE);
                    } finally {
                        FileUtils.close(is);
                    }
                }
            }
        }
    
public voidaddZipGroupFileset(org.apache.tools.ant.types.FileSet set)
Adds a group of zip files.

param
set the group (a fileset) to add

        groupfilesets.addElement(set);
    
public voidaddZipfileset(org.apache.tools.ant.types.ZipFileSet set)
Adds a set of files that can be read from an archive and be given a prefix/fullpath.

param
set the zipfileset to add

        add(set);
    
protected voidcleanUp()
Do any clean up necessary to allow this instance to be used again.

When we get here, the Zip file has been closed and all we need to do is to reset some globals.

This method will only reset globals that have been changed during execute(), it will not alter the attributes or nested child elements. If you want to reset the instance so that you can later zip a completely different set of files, you must use the reset method.

see
#reset

        addedDirs.clear();
        addedFiles.removeAllElements();
        entries.clear();
        addingNewFiles = false;
        doUpdate = savedDoUpdate;
        Enumeration e = filesetsFromGroupfilesets.elements();
        while (e.hasMoreElements()) {
            ZipFileSet zf = (ZipFileSet) e.nextElement();
            resources.removeElement(zf);
        }
        filesetsFromGroupfilesets.removeAllElements();
    
protected booleancreateEmptyZip(java.io.File zipFile)
Create an empty zip file

param
zipFile the zip file
return
true for historic reasons
throws
BuildException on error

        // In this case using java.util.zip will not work
        // because it does not permit a zero-entry archive.
        // Must create it manually.
        log("Note: creating empty " + archiveType + " archive " + zipFile,
            Project.MSG_INFO);
        OutputStream os = null;
        try {
            os = new FileOutputStream(zipFile);
            // Cf. PKZIP specification.
            byte[] empty = new byte[22];
            empty[0] = 80; // P
            empty[1] = 75; // K
            empty[2] = 5;
            empty[3] = 6;
            // remainder zeros
            os.write(empty);
        } catch (IOException ioe) {
            throw new BuildException("Could not create empty ZIP archive "
                                     + "(" + ioe.getMessage() + ")", ioe,
                                     getLocation());
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    //ignore
                }
            }
        }
        return true;
    
public voidexecute()
validate and build

throws
BuildException on error


        if (doubleFilePass) {
            skipWriting = true;
            executeMain();
            skipWriting = false;
            executeMain();
        } else {
            executeMain();
        }
    
public voidexecuteMain()
Build the zip file. This is called twice if doubleFilePass is true.

throws
BuildException on error


        if (baseDir == null && resources.size() == 0
            && groupfilesets.size() == 0 && "zip".equals(archiveType)) {
            throw new BuildException("basedir attribute must be set, "
                                     + "or at least one "
                                     + "resource collection must be given!");
        }

        if (zipFile == null) {
            throw new BuildException("You must specify the "
                                     + archiveType + " file to create!");
        }

        if (zipFile.exists() && !zipFile.isFile()) {
            throw new BuildException(zipFile + " is not a file.");
        }

        if (zipFile.exists() && !zipFile.canWrite()) {
            throw new BuildException(zipFile + " is read-only.");
        }

        // Renamed version of original file, if it exists
        File renamedFile = null;
        addingNewFiles = true;

        // Whether or not an actual update is required -
        // we don't need to update if the original file doesn't exist
        if (doUpdate && !zipFile.exists()) {
            doUpdate = false;
            log("ignoring update attribute as " + archiveType
                + " doesn't exist.", Project.MSG_DEBUG);
        }

        // Add the files found in groupfileset to fileset
        for (int i = 0; i < groupfilesets.size(); i++) {

            log("Processing groupfileset ", Project.MSG_VERBOSE);
            FileSet fs = (FileSet) groupfilesets.elementAt(i);
            FileScanner scanner = fs.getDirectoryScanner(getProject());
            String[] files = scanner.getIncludedFiles();
            File basedir = scanner.getBasedir();
            for (int j = 0; j < files.length; j++) {

                log("Adding file " + files[j] + " to fileset",
                    Project.MSG_VERBOSE);
                ZipFileSet zf = new ZipFileSet();
                zf.setProject(getProject());
                zf.setSrc(new File(basedir, files[j]));
                add(zf);
                filesetsFromGroupfilesets.addElement(zf);
            }
        }

        // collect filesets to pass them to getResourcesToAdd
        Vector vfss = new Vector();
        if (baseDir != null) {
            FileSet fs = (FileSet) getImplicitFileSet().clone();
            fs.setDir(baseDir);
            vfss.addElement(fs);
        }
        for (int i = 0; i < resources.size(); i++) {
            ResourceCollection rc = (ResourceCollection) resources.elementAt(i);
            vfss.addElement(rc);
        }

        ResourceCollection[] fss = new ResourceCollection[vfss.size()];
        vfss.copyInto(fss);
        boolean success = false;
        try {
            // can also handle empty archives
            ArchiveState state = getResourcesToAdd(fss, zipFile, false);

            // quick exit if the target is up to date
            if (!state.isOutOfDate()) {
                return;
            }

            if (!zipFile.exists() && state.isWithoutAnyResources()) {
                createEmptyZip(zipFile);
                return;
            }
            Resource[][] addThem = state.getResourcesToAdd();

            if (doUpdate) {
                renamedFile =
                    FILE_UTILS.createTempFile("zip", ".tmp",
                                              zipFile.getParentFile());
                renamedFile.deleteOnExit();

                try {
                    FILE_UTILS.rename(zipFile, renamedFile);
                } catch (SecurityException e) {
                    throw new BuildException(
                        "Not allowed to rename old file ("
                        + zipFile.getAbsolutePath()
                        + ") to temporary file");
                } catch (IOException e) {
                    throw new BuildException(
                        "Unable to rename old file ("
                        + zipFile.getAbsolutePath()
                        + ") to temporary file");
                }
            }

            String action = doUpdate ? "Updating " : "Building ";

            log(action + archiveType + ": " + zipFile.getAbsolutePath());

            ZipOutputStream zOut = null;
            try {
                if (!skipWriting) {
                    zOut = new ZipOutputStream(zipFile);

                    zOut.setEncoding(encoding);
                    zOut.setMethod(doCompress
                        ? ZipOutputStream.DEFLATED : ZipOutputStream.STORED);
                    zOut.setLevel(level);
                }
                initZipOutputStream(zOut);

                // Add the explicit resource collections to the archive.
                for (int i = 0; i < fss.length; i++) {
                    if (addThem[i].length != 0) {
                        addResources(fss[i], addThem[i], zOut);
                    }
                }

                if (doUpdate) {
                    addingNewFiles = false;
                    ZipFileSet oldFiles = new ZipFileSet();
                    oldFiles.setProject(getProject());
                    oldFiles.setSrc(renamedFile);
                    oldFiles.setDefaultexcludes(false);

                    for (int i = 0; i < addedFiles.size(); i++) {
                        PatternSet.NameEntry ne = oldFiles.createExclude();
                        ne.setName((String) addedFiles.elementAt(i));
                    }
                    DirectoryScanner ds =
                        oldFiles.getDirectoryScanner(getProject());
                    ((ZipScanner) ds).setEncoding(encoding);

                    String[] f = ds.getIncludedFiles();
                    Resource[] r = new Resource[f.length];
                    for (int i = 0; i < f.length; i++) {
                        r[i] = ds.getResource(f[i]);
                    }

                    if (!doFilesonly) {
                        String[] d = ds.getIncludedDirectories();
                        Resource[] dr = new Resource[d.length];
                        for (int i = 0; i < d.length; i++) {
                            dr[i] = ds.getResource(d[i]);
                        }
                        Resource[] tmp = r;
                        r = new Resource[tmp.length + dr.length];
                        System.arraycopy(dr, 0, r, 0, dr.length);
                        System.arraycopy(tmp, 0, r, dr.length, tmp.length);
                    }
                    addResources(oldFiles, r, zOut);
                }
                if (zOut != null) {
                    zOut.setComment(comment);
                }
                finalizeZipOutputStream(zOut);

                // If we've been successful on an update, delete the
                // temporary file
                if (doUpdate) {
                    if (!renamedFile.delete()) {
                        log ("Warning: unable to delete temporary file "
                            + renamedFile.getName(), Project.MSG_WARN);
                    }
                }
                success = true;
            } finally {
                // Close the output stream.
                try {
                    if (zOut != null) {
                        zOut.close();
                    }
                } catch (IOException ex) {
                    // If we're in this finally clause because of an
                    // exception, we don't really care if there's an
                    // exception when closing the stream. E.g. if it
                    // throws "ZIP file must have at least one entry",
                    // because an exception happened before we added
                    // any files, then we must swallow this
                    // exception. Otherwise, the error that's reported
                    // will be the close() error, which is not the
                    // real cause of the problem.
                    if (success) {
                        throw ex;
                    }
                }
            }
        } catch (IOException ioe) {
            String msg = "Problem creating " + archiveType + ": "
                + ioe.getMessage();

            // delete a bogus ZIP file (but only if it's not the original one)
            if ((!doUpdate || renamedFile != null) && !zipFile.delete()) {
                msg += " (and the archive is probably corrupt but I could not "
                    + "delete it)";
            }

            if (doUpdate && renamedFile != null) {
                try {
                    FILE_UTILS.rename(renamedFile, zipFile);
                } catch (IOException e) {
                    msg += " (and I couldn't rename the temporary file "
                            + renamedFile.getName() + " back)";
                }
            }

            throw new BuildException(msg, ioe, getLocation());
        } finally {
            cleanUp();
        }
    
protected voidfinalizeZipOutputStream(org.apache.tools.zip.ZipOutputStream zOut)
method for subclasses to override

param
zOut the zip output stream
throws
IOException on output error
throws
BuildException on other errors

    
public java.lang.StringgetComment()
Comment of the archive

return
Comment of the archive.
since
Ant 1.6.3

        return comment;
    
public java.io.FilegetDestFile()
The file to create.

return
the destination file
since
Ant 1.5.2

        return zipFile;
    
public java.lang.StringgetEncoding()
Encoding to use for filenames.

return
the name of the encoding to use
since
Ant 1.5.2

        return encoding;
    
public intgetLevel()
Get the compression level.

return
compression level.
since
Ant 1.7

        return level;
    
protected org.apache.tools.ant.taskdefs.Zip$ArchiveStategetNonFileSetResourcesToAdd(org.apache.tools.ant.types.ResourceCollection[] rcs, java.io.File zipFile, boolean needsUpdate)
Collect the resources that are newer than the corresponding entries (or missing) in the original archive.

If we are going to recreate the archive instead of updating it, all resources should be considered as new, if a single one is. Because of this, subclasses overriding this method must call super.getResourcesToAdd and indicate with the third arg if they already know that the archive is out-of-date.

param
rcs The filesets to grab resources from
param
zipFile intended archive file (may or may not exist)
param
needsUpdate whether we already know that the archive is out-of-date. Subclasses overriding this method are supposed to set this value correctly in their call to super.getResourcesToAdd.
return
an array of resources to add for each fileset passed in as well as a flag that indicates whether the archive is uptodate.
exception
BuildException if it likes

        /*
         * Backwards compatibility forces us to repeat the logic of
         * getResourcesToAdd(FileSet[], ...) here once again.
         */

        Resource[][] initialResources = grabNonFileSetResources(rcs);
        if (isEmpty(initialResources)) {
            // no emptyBehavior handling since the FileSet version
            // will take care of it.
            return new ArchiveState(needsUpdate, initialResources);
        }

        // initialResources is not empty

        if (!zipFile.exists()) {
            return new ArchiveState(true, initialResources);
        }

        if (needsUpdate && !doUpdate) {
            // we are recreating the archive, need all resources
            return new ArchiveState(true, initialResources);
        }

        Resource[][] newerResources = new Resource[rcs.length][];

        for (int i = 0; i < rcs.length; i++) {
            if (initialResources[i].length == 0) {
                newerResources[i] = new Resource[] {};
                continue;
            }

            for (int j = 0; j < initialResources[i].length; j++) {
                if (initialResources[i][j] instanceof FileResource
                    && zipFile.equals(((FileResource)
                                       initialResources[i][j]).getFile())) {
                    throw new BuildException("A zip file cannot include "
                                             + "itself", getLocation());
                }
            }

            Resource[] rs = initialResources[i];
            if (doFilesonly) {
                rs = selectFileResources(rs);
            }

            newerResources[i] =
                ResourceUtils.selectOutOfDateSources(this,
                                                     rs,
                                                     new IdentityMapper(),
                                                     getZipScanner());
            needsUpdate = needsUpdate || (newerResources[i].length > 0);

            if (needsUpdate && !doUpdate) {
                // we will return initialResources anyway, no reason
                // to scan further.
                break;
            }
        }

        if (needsUpdate && !doUpdate) {
            // we are recreating the archive, need all resources
            return new ArchiveState(true, initialResources);
        }

        return new ArchiveState(needsUpdate, newerResources);
    
protected org.apache.tools.ant.taskdefs.Zip$ArchiveStategetResourcesToAdd(org.apache.tools.ant.types.ResourceCollection[] rcs, java.io.File zipFile, boolean needsUpdate)
Collect the resources that are newer than the corresponding entries (or missing) in the original archive.

If we are going to recreate the archive instead of updating it, all resources should be considered as new, if a single one is. Because of this, subclasses overriding this method must call super.getResourcesToAdd and indicate with the third arg if they already know that the archive is out-of-date.

This method first delegates to getNonFileSetResourceToAdd and then invokes the FileSet-arg version. All this to keep backwards compatibility for subclasses that don't know how to deal with non-FileSet ResourceCollections.

param
rcs The resource collections to grab resources from
param
zipFile intended archive file (may or may not exist)
param
needsUpdate whether we already know that the archive is out-of-date. Subclasses overriding this method are supposed to set this value correctly in their call to super.getResourcesToAdd.
return
an array of resources to add for each fileset passed in as well as a flag that indicates whether the archive is uptodate.
exception
BuildException if it likes
since
Ant 1.7

        ArrayList filesets = new ArrayList();
        ArrayList rest = new ArrayList();
        for (int i = 0; i < rcs.length; i++) {
            if (rcs[i] instanceof FileSet) {
                filesets.add(rcs[i]);
            } else {
                rest.add(rcs[i]);
            }
        }
        ResourceCollection[] rc = (ResourceCollection[])
            rest.toArray(new ResourceCollection[rest.size()]);
        ArchiveState as = getNonFileSetResourcesToAdd(rc, zipFile,
                                                      needsUpdate);

        FileSet[] fs = (FileSet[]) filesets.toArray(new FileSet[filesets
                                                                .size()]);
        ArchiveState as2 = getResourcesToAdd(fs, zipFile, as.isOutOfDate());
        if (!as.isOutOfDate() && as2.isOutOfDate()) {
            /*
             * Bad luck.
             *
             * There are resources in the filesets that make the
             * archive out of date, but not in the non-fileset
             * resources. We need to rescan the non-FileSets to grab
             * all of them now.
             */
            as = getNonFileSetResourcesToAdd(rc, zipFile, true);
        }

        Resource[][] toAdd = new Resource[rcs.length][];
        int fsIndex = 0;
        int restIndex = 0;
        for (int i = 0; i < rcs.length; i++) {
            if (rcs[i] instanceof FileSet) {
                toAdd[i] = as2.getResourcesToAdd()[fsIndex++];
            } else {
                toAdd[i] = as.getResourcesToAdd()[restIndex++];
            }
        }
        return new ArchiveState(as2.isOutOfDate(), toAdd);
    
protected org.apache.tools.ant.taskdefs.Zip$ArchiveStategetResourcesToAdd(org.apache.tools.ant.types.FileSet[] filesets, java.io.File zipFile, boolean needsUpdate)
Collect the resources that are newer than the corresponding entries (or missing) in the original archive.

If we are going to recreate the archive instead of updating it, all resources should be considered as new, if a single one is. Because of this, subclasses overriding this method must call super.getResourcesToAdd and indicate with the third arg if they already know that the archive is out-of-date.

param
filesets The filesets to grab resources from
param
zipFile intended archive file (may or may not exist)
param
needsUpdate whether we already know that the archive is out-of-date. Subclasses overriding this method are supposed to set this value correctly in their call to super.getResourcesToAdd.
return
an array of resources to add for each fileset passed in as well as a flag that indicates whether the archive is uptodate.
exception
BuildException if it likes


        Resource[][] initialResources = grabResources(filesets);
        if (isEmpty(initialResources)) {
            if (needsUpdate && doUpdate) {
                /*
                 * This is a rather hairy case.
                 *
                 * One of our subclasses knows that we need to update the
                 * archive, but at the same time, there are no resources
                 * known to us that would need to be added.  Only the
                 * subclass seems to know what's going on.
                 *
                 * This happens if <jar> detects that the manifest has changed,
                 * for example.  The manifest is not part of any resources
                 * because of our support for inline <manifest>s.
                 *
                 * If we invoke createEmptyZip like Ant 1.5.2 did,
                 * we'll loose all stuff that has been in the original
                 * archive (bugzilla report 17780).
                 */
                return new ArchiveState(true, initialResources);
            }

            if (emptyBehavior.equals("skip")) {
                if (doUpdate) {
                    log(archiveType + " archive " + zipFile
                        + " not updated because no new files were included.",
                        Project.MSG_VERBOSE);
                } else {
                    log("Warning: skipping " + archiveType + " archive "
                        + zipFile + " because no files were included.",
                        Project.MSG_WARN);
                }
            } else if (emptyBehavior.equals("fail")) {
                throw new BuildException("Cannot create " + archiveType
                                         + " archive " + zipFile
                                         + ": no files were included.",
                                         getLocation());
            } else {
                // Create.
                if (!zipFile.exists())  {
                    needsUpdate = true;
                }
            }
            return new ArchiveState(needsUpdate, initialResources);
        }

        // initialResources is not empty

        if (!zipFile.exists()) {
            return new ArchiveState(true, initialResources);
        }

        if (needsUpdate && !doUpdate) {
            // we are recreating the archive, need all resources
            return new ArchiveState(true, initialResources);
        }

        Resource[][] newerResources = new Resource[filesets.length][];

        for (int i = 0; i < filesets.length; i++) {
            if (!(fileset instanceof ZipFileSet)
                || ((ZipFileSet) fileset).getSrc(getProject()) == null) {
                File base = filesets[i].getDir(getProject());

                for (int j = 0; j < initialResources[i].length; j++) {
                    File resourceAsFile =
                        FILE_UTILS.resolveFile(base,
                                              initialResources[i][j].getName());
                    if (resourceAsFile.equals(zipFile)) {
                        throw new BuildException("A zip file cannot include "
                                                 + "itself", getLocation());
                    }
                }
            }
        }

        for (int i = 0; i < filesets.length; i++) {
            if (initialResources[i].length == 0) {
                newerResources[i] = new Resource[] {};
                continue;
            }

            FileNameMapper myMapper = new IdentityMapper();
            if (filesets[i] instanceof ZipFileSet) {
                ZipFileSet zfs = (ZipFileSet) filesets[i];
                if (zfs.getFullpath(getProject()) != null
                    && !zfs.getFullpath(getProject()).equals("")) {
                    // in this case all files from origin map to
                    // the fullPath attribute of the zipfileset at
                    // destination
                    MergingMapper fm = new MergingMapper();
                    fm.setTo(zfs.getFullpath(getProject()));
                    myMapper = fm;

                } else if (zfs.getPrefix(getProject()) != null
                           && !zfs.getPrefix(getProject()).equals("")) {
                    GlobPatternMapper gm = new GlobPatternMapper();
                    gm.setFrom("*");
                    String prefix = zfs.getPrefix(getProject());
                    if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
                        prefix += "/";
                    }
                    gm.setTo(prefix + "*");
                    myMapper = gm;
                }
            }

            Resource[] resources = initialResources[i];
            if (doFilesonly) {
                resources = selectFileResources(resources);
            }

            newerResources[i] =
                ResourceUtils.selectOutOfDateSources(this,
                                                     resources,
                                                     myMapper,
                                                     getZipScanner());
            needsUpdate = needsUpdate || (newerResources[i].length > 0);

            if (needsUpdate && !doUpdate) {
                // we will return initialResources anyway, no reason
                // to scan further.
                break;
            }
        }

        if (needsUpdate && !doUpdate) {
            // we are recreating the archive, need all resources
            return new ArchiveState(true, initialResources);
        }

        return new ArchiveState(needsUpdate, newerResources);
    
private synchronized org.apache.tools.ant.types.ZipScannergetZipScanner()

since
Ant 1.5.2

        if (zs == null) {
            zs = new ZipScanner();
            zs.setEncoding(encoding);
            zs.setSrc(zipFile);
        }
        return zs;
    
protected org.apache.tools.ant.types.Resource[][]grabNonFileSetResources(org.apache.tools.ant.types.ResourceCollection[] rcs)
Fetch all included and not excluded resources from the collections.

Included directories will precede included files.

param
rcs an array of resource collections
return
the resources included
since
Ant 1.7

        Resource[][] result = new Resource[rcs.length][];
        for (int i = 0; i < rcs.length; i++) {
            Iterator iter = rcs[i].iterator();
            ArrayList rs = new ArrayList();
            int lastDir = 0;
            while (iter.hasNext()) {
                Resource r = (Resource) iter.next();
                if (r.isExists()) {
                    if (r.isDirectory()) {
                        rs.add(lastDir++, r);
                    } else {
                        rs.add(r);
                    }
                }
            }
            result[i] = (Resource[]) rs.toArray(new Resource[rs.size()]);
        }
        return result;
    
protected org.apache.tools.ant.types.Resource[][]grabResources(org.apache.tools.ant.types.FileSet[] filesets)
Fetch all included and not excluded resources from the sets.

Included directories will precede included files.

param
filesets an array of filesets
return
the resources included
since
Ant 1.5.2

        Resource[][] result = new Resource[filesets.length][];
        for (int i = 0; i < filesets.length; i++) {
            boolean skipEmptyNames = true;
            if (filesets[i] instanceof ZipFileSet) {
                ZipFileSet zfs = (ZipFileSet) filesets[i];
                skipEmptyNames = zfs.getPrefix(getProject()).equals("")
                    && zfs.getFullpath(getProject()).equals("");
            }
            DirectoryScanner rs =
                filesets[i].getDirectoryScanner(getProject());
            if (rs instanceof ZipScanner) {
                ((ZipScanner) rs).setEncoding(encoding);
            }
            Vector resources = new Vector();
            if (!doFilesonly) {
                String[] directories = rs.getIncludedDirectories();
                for (int j = 0; j < directories.length; j++) {
                    if (!"".equals(directories[j]) || !skipEmptyNames) {
                        resources.addElement(rs.getResource(directories[j]));
                    }
                }
            }
            String[] files = rs.getIncludedFiles();
            for (int j = 0; j < files.length; j++) {
                if (!"".equals(files[j]) || !skipEmptyNames) {
                    resources.addElement(rs.getResource(files[j]));
                }
            }

            result[i] = new Resource[resources.size()];
            resources.copyInto(result[i]);
        }
        return result;
    
protected voidinitZipOutputStream(org.apache.tools.zip.ZipOutputStream zOut)
method for subclasses to override

param
zOut the zip output stream
throws
IOException on output error
throws
BuildException on other errors

    
protected final booleanisAddingNewFiles()
Indicates if the task is adding new files into the archive as opposed to copying back unchanged files from the backup copy

return
true if adding new files

        return addingNewFiles;
    
public booleanisCompress()
Whether we want to compress the files or only store them;

return
true if the files are to be compressed
since
Ant 1.5.2

        return doCompress;
    
protected static final booleanisEmpty(org.apache.tools.ant.types.Resource[][] r)
Check is the resource arrays are empty.

param
r the arrays to check
return
true if all individual arrays are empty
since
Ant 1.5.2

        for (int i = 0; i < r.length; i++) {
            if (r[i].length > 0) {
                return false;
            }
        }
        return true;
    
public booleanisInUpdateMode()
Are we updating an existing archive?

return
true if updating an existing archive

        return doUpdate;
    
public voidreset()
Makes this instance reset all attributes to their default values and forget all children.

since
Ant 1.5
see
#cleanUp

        resources.removeAllElements();
        zipFile = null;
        baseDir = null;
        groupfilesets.removeAllElements();
        duplicate = "add";
        archiveType = "zip";
        doCompress = true;
        emptyBehavior = "skip";
        doUpdate = false;
        doFilesonly = false;
        encoding = null;
    
protected org.apache.tools.ant.types.Resource[]selectFileResources(org.apache.tools.ant.types.Resource[] orig)
Drops all non-file resources from the given array.

param
orig the resources to filter
return
the filters resources
since
Ant 1.6

        if (orig.length == 0) {
            return orig;
        }

        Vector v = new Vector(orig.length);
        for (int i = 0; i < orig.length; i++) {
            if (!orig[i].isDirectory()) {
                v.addElement(orig[i]);
            } else {
                log("Ignoring directory " + orig[i].getName()
                    + " as only files will be added.", Project.MSG_VERBOSE);
            }
        }

        if (v.size() != orig.length) {
            Resource[] r = new Resource[v.size()];
            v.copyInto(r);
            return r;
        }
        return orig;
    
public voidsetBasedir(java.io.File baseDir)
Directory from which to archive files; optional.

param
baseDir the base directory

        this.baseDir = baseDir;
    
public voidsetComment(java.lang.String comment)
Comment to use for archive.

param
comment The content of the comment.
since
Ant 1.6.3

        this.comment = comment;
    
public voidsetCompress(boolean c)
Whether we want to compress the files or only store them; optional, default=true;

param
c if true, compress the files

        doCompress = c;
    
public voidsetDestFile(java.io.File destFile)
The file to create; required.

since
Ant 1.5
param
destFile The new destination File

       this.zipFile = destFile;
    
public voidsetDuplicate(org.apache.tools.ant.taskdefs.Zip$Duplicate df)
Sets behavior for when a duplicate file is about to be added - one of add, preserve or fail. Possible values are: add (keep both of the files); preserve (keep the first version of the file found); fail halt a problem Default for zip tasks is add

param
df a Duplicate enumerated value

        duplicate = df.getValue();
    
public voidsetEncoding(java.lang.String encoding)
Encoding to use for filenames, defaults to the platform's default encoding.

For a list of possible values see http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html.

param
encoding the encoding name

        this.encoding = encoding;
    
public voidsetFile(java.io.File file)
This is the name/location of where to create the file.

param
file the path of the zipFile
since
Ant 1.5
deprecated
since 1.5.x. Use setDestFile(File) instead.
ant.attribute
ignore="true"

        setDestFile(file);
    
public voidsetFilesonly(boolean f)
If true, emulate Sun's jar utility by not adding parent directories; optional, defaults to false.

param
f if true, emulate sun's jar by not adding parent directories

        doFilesonly = f;
    
public voidsetKeepCompression(boolean keep)
Whether the original compression of entries coming from a ZIP archive should be kept (for example when updating an archive). Default is false.

param
keep if true, keep the original compression
since
Ant 1.6

        keepCompression = keep;
    
public voidsetLevel(int level)
Set the compression level to use. Default is ZipOutputStream.DEFAULT_COMPRESSION.

param
level compression level.
since
Ant 1.7

        this.level = level;
    
public voidsetRoundUp(boolean r)
Whether the file modification times will be rounded up to the next even number of seconds.

Zip archives store file modification times with a granularity of two seconds, so the times will either be rounded up or down. If you round down, the archive will always seem out-of-date when you rerun the task, so the default is to round up. Rounding up may lead to a different type of problems like JSPs inside a web archive that seem to be slightly more recent than precompiled pages, rendering precompilation useless.

param
r a boolean value
since
Ant 1.6.2

        roundUp = r;
    
public voidsetUpdate(boolean c)
If true, updates an existing file, otherwise overwrite any existing one; optional defaults to false.

param
c if true, updates an existing zip file

        doUpdate = c;
        savedDoUpdate = c;
    
public voidsetWhenempty(org.apache.tools.ant.taskdefs.Zip$WhenEmpty we)
Sets behavior of the task when no files match. Possible values are: fail (throw an exception and halt the build); skip (do not create any archive, but issue a warning); create (make an archive with no entries). Default for zip tasks is skip; for jar tasks, create.

param
we a WhenEmpty enumerated value

        emptyBehavior = we.getValue();
    
public voidsetZipfile(java.io.File zipFile)
This is the name/location of where to create the .zip file.

param
zipFile the path of the zipFile
deprecated
since 1.5.x. Use setDestFile(File) instead.
ant.attribute
ignore="true"


                                               
        
        setDestFile(zipFile);
    
protected voidzipDir(java.io.File dir, org.apache.tools.zip.ZipOutputStream zOut, java.lang.String vPath, int mode)
Add a directory to the zip stream.

param
dir the directort to add to the archive
param
zOut the stream to write to
param
vPath the name this entry shall have in the archive
param
mode the Unix permissions to set.
throws
IOException on error
since
Ant 1.5.2

        zipDir(dir, zOut, vPath, mode, null);
    
protected voidzipDir(java.io.File dir, org.apache.tools.zip.ZipOutputStream zOut, java.lang.String vPath, int mode, org.apache.tools.zip.ZipExtraField[] extra)
Add a directory to the zip stream.

param
dir the directort to add to the archive
param
zOut the stream to write to
param
vPath the name this entry shall have in the archive
param
mode the Unix permissions to set.
param
extra ZipExtraFields to add
throws
IOException on error
since
Ant 1.6.3

        if (doFilesonly) {
            log("skipping directory " + vPath + " for file-only archive",
                    Project.MSG_VERBOSE);
            return;
        }
        if (addedDirs.get(vPath) != null) {
            // don't add directories we've already added.
            // no warning if we try, it is harmless in and of itself
            return;
        }

        log("adding directory " + vPath, Project.MSG_VERBOSE);
        addedDirs.put(vPath, vPath);

        if (!skipWriting) {
            ZipEntry ze = new ZipEntry (vPath);
            if (dir != null && dir.exists()) {
                // ZIPs store time with a granularity of 2 seconds, round up
                ze.setTime(dir.lastModified() + (roundUp ? 1999 : 0));
            } else {
                // ZIPs store time with a granularity of 2 seconds, round up
                ze.setTime(System.currentTimeMillis() + (roundUp ? 1999 : 0));
            }
            ze.setSize (0);
            ze.setMethod (ZipEntry.STORED);
            // This is faintly ridiculous:
            ze.setCrc (EMPTY_CRC);
            ze.setUnixMode(mode);

            if (extra != null) {
                ze.setExtraFields(extra);
            }

            zOut.putNextEntry(ze);
        }
    
protected voidzipFile(java.io.InputStream in, org.apache.tools.zip.ZipOutputStream zOut, java.lang.String vPath, long lastModified, java.io.File fromArchive, int mode)
Adds a new entry to the archive, takes care of duplicates as well.

param
in the stream to read data for the entry from.
param
zOut the stream to write to.
param
vPath the name this entry shall have in the archive.
param
lastModified last modification time for the entry.
param
fromArchive the original archive we are copying this entry from, will be null if we are not copying from an archive.
param
mode the Unix permissions to set.
since
Ant 1.5.2
throws
IOException on error

        if (entries.contains(vPath)) {

            if (duplicate.equals("preserve")) {
                log(vPath + " already added, skipping", Project.MSG_INFO);
                return;
            } else if (duplicate.equals("fail")) {
                throw new BuildException("Duplicate file " + vPath
                                         + " was found and the duplicate "
                                         + "attribute is 'fail'.");
            } else {
                // duplicate equal to add, so we continue
                log("duplicate file " + vPath
                    + " found, adding.", Project.MSG_VERBOSE);
            }
        } else {
            log("adding entry " + vPath, Project.MSG_VERBOSE);
        }

        entries.put(vPath, vPath);

        if (!skipWriting) {
            ZipEntry ze = new ZipEntry(vPath);
            ze.setTime(lastModified);
            ze.setMethod(doCompress ? ZipEntry.DEFLATED : ZipEntry.STORED);

            /*
             * ZipOutputStream.putNextEntry expects the ZipEntry to
             * know its size and the CRC sum before you start writing
             * the data when using STORED mode - unless it is seekable.
             *
             * This forces us to process the data twice.
             */
            if (!zOut.isSeekable() && !doCompress) {
                long size = 0;
                CRC32 cal = new CRC32();
                if (!in.markSupported()) {
                    // Store data into a byte[]
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();

                    byte[] buffer = new byte[8 * 1024];
                    int count = 0;
                    do {
                        size += count;
                        cal.update(buffer, 0, count);
                        bos.write(buffer, 0, count);
                        count = in.read(buffer, 0, buffer.length);
                    } while (count != -1);
                    in = new ByteArrayInputStream(bos.toByteArray());

                } else {
                    in.mark(Integer.MAX_VALUE);
                    byte[] buffer = new byte[8 * 1024];
                    int count = 0;
                    do {
                        size += count;
                        cal.update(buffer, 0, count);
                        count = in.read(buffer, 0, buffer.length);
                    } while (count != -1);
                    in.reset();
                }
                ze.setSize(size);
                ze.setCrc(cal.getValue());
            }

            ze.setUnixMode(mode);
            zOut.putNextEntry(ze);

            byte[] buffer = new byte[8 * 1024];
            int count = 0;
            do {
                if (count != 0) {
                    zOut.write(buffer, 0, count);
                }
                count = in.read(buffer, 0, buffer.length);
            } while (count != -1);
        }
        addedFiles.addElement(vPath);
    
protected voidzipFile(java.io.File file, org.apache.tools.zip.ZipOutputStream zOut, java.lang.String vPath, int mode)
Method that gets called when adding from java.io.File instances.

This implementation delegates to the six-arg version.

param
file the file to add to the archive
param
zOut the stream to write to
param
vPath the name this entry shall have in the archive
param
mode the Unix permissions to set.
throws
IOException on error
since
Ant 1.5.2

        if (file.equals(zipFile)) {
            throw new BuildException("A zip file cannot include itself",
                                     getLocation());
        }

        FileInputStream fIn = new FileInputStream(file);
        try {
            // ZIPs store time with a granularity of 2 seconds, round up
            zipFile(fIn, zOut, vPath,
                    file.lastModified() + (roundUp ? 1999 : 0),
                    null, mode);
        } finally {
            fIn.close();
        }