bb.io
Class ZipUtil

java.lang.Object
  extended by bb.io.ZipUtil

public final class ZipUtil
extends Object

Provides static utility methods for dealing with ZIP paths.

This class is multithread safe: it is immutable (both its immediate state, as well as the deep state of its fields).

Author:
Brent Boyer
See Also:
The ZIP file specification, DDJ article on Java and ZIP, The ZipCreate program, Self-extracting JAR files, "The O'Reilly Java I/O book"

Nested Class Summary
static class ZipUtil.UnitTest
          See the Overview page of the project's javadocs for a general description of this unit test class.
 
Field Summary
private static String appendAll_key
           
private static String appendBackup_key
           
private static String appendExtension_key
           
private static String appendTimeStamp_key
           
private static String directoryExtraction_key
           
private static String filter_key
           
private static long fourGibi
          Is 4 * 2^30 = 2^32 = .
private static boolean giveUserFeedback
           
private static List<String> keysLegal_archive
          Specifies all the switch keys which can legally appear as command line arguments to main for an archive operation.
private static List<String> keysLegal_extract
          Specifies all the switch keys which can legally appear as command line arguments to main for an extract operation.
private static List<String> keysLegal_listContents
          Specifies all the switch keys which can legally appear as command line arguments to main for a content list operation.
private static String listContents_key
           
private static String overwrite_key
           
private static String pathsToArchive_key
           
private static long zipableFileSizeLimit
          Maximum size of a file that can be put into a ZIP archive file by Java.
private static long zipArchiveSizeLimit
          Maximum size of any ZIP archive file that can be read by Java.
private static String zipFile_key
           
 
Constructor Summary
private ZipUtil()
          This sole private constructor suppresses the default (public) constructor, ensuring non-instantiability outside of this class.
 
Method Summary
static void archive(File zipFile, FileFilter filter, File... pathsToArchive)
          Writes each element of pathsToArchive to a new ZIP format archive file specified by zipFile.
private static void archive(File path, FileParent fileParent, ZipOutputStream zipOutputStream, FileFilter filter)
          Writes path as a new ZipEntry to the ZipOutputStream.
static void extract(File zipFile, File directoryExtraction, boolean overwrite)
          Creates a ZipFile out of zipFile named zipApiFile, and then calls extract( zipApiFile, directoryExtraction, overwrite ).
static void extract(ZipFile zipApiFile, File directoryExtraction, boolean overwrite)
          Extracts the contents of zipApiFile to directoryExtraction.
(package private) static File getArchiveFile(Properties2 switches, String key, String extension)
           
static ZipEntry[] getEntries(File zipFile, boolean sortResult)
          Creates a ZipFile out of zipFile named zipApiFile, and then returns getEntries( zipApiFile, sortResult ).
static ZipEntry[] getEntries(ZipFile zipApiFile, boolean sortResult)
          Returns all the ZipEntrys in zipApiFile.
(package private) static FileFilter getFileFilter(Properties2 switches)
           
(package private) static File[] getPathsToArchive(Properties2 switches)
           
static boolean isZipable(File path)
          If path is a directory, then returns true.
static void main(String[] args)
          May be used either to list the contents of, archive to, or extract from a ZIP file.
private static void readInFile(File path, OutputStream out)
          Reads all the bytes from path and writes them to out.
private static void writeOutFile(ZipFile zipApiFile, ZipEntry entry, File path)
          Writes all the bytes from zipApiFile's current entry to path.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

zipFile_key

private static final String zipFile_key
See Also:
Constant Field Values

listContents_key

private static final String listContents_key
See Also:
Constant Field Values

pathsToArchive_key

private static final String pathsToArchive_key
See Also:
Constant Field Values

appendBackup_key

private static final String appendBackup_key
See Also:
Constant Field Values

appendTimeStamp_key

private static final String appendTimeStamp_key
See Also:
Constant Field Values

appendExtension_key

private static final String appendExtension_key
See Also:
Constant Field Values

appendAll_key

private static final String appendAll_key
See Also:
Constant Field Values

filter_key

private static final String filter_key
See Also:
Constant Field Values

directoryExtraction_key

private static final String directoryExtraction_key
See Also:
Constant Field Values

overwrite_key

private static final String overwrite_key
See Also:
Constant Field Values

keysLegal_listContents

private static final List<String> keysLegal_listContents
Specifies all the switch keys which can legally appear as command line arguments to main for a content list operation.


keysLegal_archive

private static final List<String> keysLegal_archive
Specifies all the switch keys which can legally appear as command line arguments to main for an archive operation.


keysLegal_extract

private static final List<String> keysLegal_extract
Specifies all the switch keys which can legally appear as command line arguments to main for an extract operation.


fourGibi

private static final long fourGibi
Is 4 * 2^30 = 2^32 = .

See Also:
Constant Field Values

zipableFileSizeLimit

private static final long zipableFileSizeLimit
Maximum size of a file that can be put into a ZIP archive file by Java. This value is valid as of 2005/1/16 under JDK 1.5.0.

See Also:
bug report #1, bug report #2, "The file D:\software\java\proposalsQuestionsPostingsEtc\zipFileSupportInJava.txt", Constant Field Values

zipArchiveSizeLimit

private static final long zipArchiveSizeLimit
Maximum size of any ZIP archive file that can be read by Java. This value is valid as of 2005/1/16 under JDK 1.5.0.

See Also:
Discussion #1 of the official ZIP standard, Discussion #2 of the official ZIP standard, Constant Field Values

giveUserFeedback

private static final boolean giveUserFeedback
See Also:
Constant Field Values
Constructor Detail

ZipUtil

private ZipUtil()
This sole private constructor suppresses the default (public) constructor, ensuring non-instantiability outside of this class.

Method Detail

main

public static void main(String[] args)
May be used either to list the contents of, archive to, or extract from a ZIP file. The action to perform and all of its specifications are embedded as command line switches in args.

If listing archive contents, the action to perform (listing) is specified as the (key-only) command line switch -listContents. The target ZIP archive file is the (key/value) command line switch -zipFile insertPathHere. For example, here is a complete command line that lists the contents of a zip file located in the working directory:


                java  bb.io.ZipUtil  -zipFile ./backup.zip  -listContents
 

If archiving, the source path(s) to be archived (which can be either normal files or directories) are specified as the (key/value) command line switch -pathsToArchive commaSeparatedListOfPaths. The target ZIP archive file is the same -zipFile command line switch mentioned before. The following optional switches may also be supplied:

  1. -appendBackup appends _backup to the ZIP file's name
  2. -appendTimeStamp appends _ followed by a timestamp to the ZIP file's name
  3. -appendExtension appends .zip to the ZIP file's name
  4. -appendAll is equivalent to supplying all the above append options
  5. -filter fullyQualifiedClassName specifies the name of a FileFilter which limits what gets archived. Since this FileFilter class will be instantiated by a call to Class.forName, it must have a no-arg constructor.
For example, here is a complete command line that archives just the class files found under two different class directories:

                java  bb.io.ZipUtil  -zipFile ../log/test.zip  -pathsToArchive ../class1,../class2  -filter bb.io.filefilter.ClassFilter
 

If extracting, the target directory to extract into is always specified as the command line switch -directoryExtraction insertPathHere. The source ZIP archive file is the same -zipFile command line switch mentioned before. An optional switch -overwrite true/false may also be supplied to control if overwriting of existing normal files is allowed or not. By default overwriting is not allowed (an Exception will be thrown if extraction needs to overwrite an existing file). For example, here is a complete command line that extracts a ZIP file to a specific directory, overwriting any existing files:


                java  bb.io.ZipUtil  -zipFile ../log/test.zip  -directoryExtraction ../log/zipExtractOutput  -overwrite true
 

Note that the switches may appear in any order on the command line.

If this method is this Java process's entry point (i.e. first main method), then its final action is a call to System.exit, which means that this method never returns; its exit code is 0 if it executes normally, 1 if it throws a Throwable (which will be caught and logged). Otherwise, this method returns and leaves the JVM running.


getArchiveFile

static File getArchiveFile(Properties2 switches,
                           String key,
                           String extension)
                    throws Exception
Throws:
Exception

getFileFilter

static FileFilter getFileFilter(Properties2 switches)
                         throws Exception
Throws:
Exception

getPathsToArchive

static File[] getPathsToArchive(Properties2 switches)
                         throws Exception
Throws:
Exception

isZipable

public static boolean isZipable(File path)
                         throws IllegalArgumentException,
                                SecurityException
If path is a directory, then returns true. Else if path is a normal file, then returns true if path's length (in bytes) is <= zipableFileSizeLimit, false otherwise. Else returns false.

Throws:
IllegalArgumentException - if path == null or path does not exist
SecurityException - if a security manager exists and its SecurityManager.checkRead method denies read access to path

archive

public static void archive(File zipFile,
                           FileFilter filter,
                           File... pathsToArchive)
                    throws Exception
Writes each element of pathsToArchive to a new ZIP format archive file specified by zipFile. If any element is a directory, the entire contents of its directory tree will be archived (as limited by filter). Paths that would otherwise be archived may be screened out by supplying a non null value for filter.

Altho this method does not use DirUtil.getTree, it uses filter to control subdirectory exploration in a similar manner.

In general, the path stored in the archive is the path relative to the parent of the relevant element of pathsToArchive. For example, suppose that some element of pathsToArchive corresponds to D:/someDirectory, and suppose that that directory contains the subdirectory and child file D:/someDirectory/anotherDirectory/childFile. Then the paths stored in the archive are anotherDirectory and anotherDirectory/childFile respectively.

One complication with the above scheme is paths which are file system roots: they have no parents. Examples include the windows path C: or the unix path /. In cases like these, this method uses an imaginary parent name of the form rootXXX (where XXX is an integer). For example, on a windows machine, if pathsToArchive contains the paths C: and D:, then the contents of C: might be stored in the archive with a path that starts with root1, and the contents of D: may have an archive path that starts with root2. This behavior ensures that the archive preserves the separate origins of the 2 sources, which is necessary so that they do not get mixed when extracted.

Parameters:
zipFile - the ZIP File that will write the archive data to
filter - a FileFilter that can use to screen out certain paths from being written to the archive; may be null (so everything specified by pathsToArchive gets archived); if not null, see warnings in DirUtil.getTree on directory acceptance
pathsToArchive - array of all the paths to archive
Throws:
Exception - if any Throwable is caught; the Throwable is stored as the cause, and the message stores the path of zipFile; here are some of the possible causes:
  1. IllegalArgumentException if pathsToArchive == null; pathsToArchive.length == 0; zipFile == null; zipFile already exists and either is not a normal file or is but already has data inside it; zipFile has an invalid extension; any element of pathsToArchive is null, does not exist, cannot be read, is equal to zipFile, its path contains zipFile, or it fails isZipable
  2. SecurityException if a security manager exists and its SecurityManager.checkRead method denies read access to some path
  3. IOException if an I/O problem occurs

archive

private static void archive(File path,
                            FileParent fileParent,
                            ZipOutputStream zipOutputStream,
                            FileFilter filter)
                     throws Exception
Writes path as a new ZipEntry to the ZipOutputStream. If path is a normal file, then next writes path's data to zipOutputStream. This ZipEntry is always written compressed, and at the highest compression level.

If path is a directory, then this method additionally calls itself on the contents (thus recursing thru the entire directory tree).

Warning: several popular programs (e.g. winzip) fail to display mere directory entries. Furthermore, if just a directory entry is present (i.e. it is empty), they also may fail to create a new empty directoy when extracting the ZIP file's contents. These are bugs in their behavior.

An optional FileFilter can be supplied to screen out paths that would otherwise be archived.

This method does not close zipOutputStream: that is the responsibility of the caller.

The caller also must take on the responsibility to not do anything stupid, like write path more than once, or have the path be the same File that zipOutputStream is writing to.

Parameters:
path - the File to archive
fileParent - the FileParent for path
zipOutputStream - the ZipOutputStream that will write the archive data to
filter - a FileFilter that can use to screen out certain files from being written to the archive; may be null (so everything specified by path gets archived)
Throws:
Exception - if any Throwable is caught; the Throwable is stored as the cause, and the message stores path's information; here are some of the possible causes:
  1. IllegalArgumentException if path fails isZipable
  2. SecurityException if a security manager exists and its SecurityManager.checkRead method denies read access to path
  3. IOException if an I/O problem occurs

readInFile

private static void readInFile(File path,
                               OutputStream out)
                        throws IOException
Reads all the bytes from path and writes them to out.

Throws:
IOException - if an I/O problem occurs

getEntries

public static ZipEntry[] getEntries(File zipFile,
                                    boolean sortResult)
                             throws IllegalArgumentException,
                                    IOException
Creates a ZipFile out of zipFile named zipApiFile, and then returns getEntries( zipApiFile, sortResult ). Final action is to close zipApiFile.

Parameters:
zipFile - the ZIP format file to be read
sortResult - if true, then the result is first sorted by each entry's name before return; otherwise the order is the sequence read from zipFile
Throws:
IllegalArgumentException - if zipFile fails Check.validFile
IOException - if an I/O problem occurs

getEntries

public static ZipEntry[] getEntries(ZipFile zipApiFile,
                                    boolean sortResult)
                             throws IllegalArgumentException,
                                    IllegalStateException
Returns all the ZipEntrys in zipApiFile.

Warning: zipApiFile is not closed by this method.

Parameters:
zipApiFile - the ZipFile to get the entries from
sortResult - if true, then the result ZipEntry[] is first sorted by each entry's name before return; otherwise the order is the sequence read from zipApiFile
Throws:
IllegalArgumentException - if zipApiFile == null
IllegalStateException - if zipApiFile has been closed

extract

public static void extract(File zipFile,
                           File directoryExtraction,
                           boolean overwrite)
                    throws IllegalArgumentException,
                           SecurityException,
                           IllegalStateException,
                           IOException
Creates a ZipFile out of zipFile named zipApiFile, and then calls extract( zipApiFile, directoryExtraction, overwrite ). Final action is to close zipApiFile.

Parameters:
zipFile - the ZIP format file to be read
directoryExtraction - the directory that will extract the contents of zipApiFile into
overwrite - specifies whether or not extraction is allowed to overwrite an existing normal file inside directoryExtraction
Throws:
IllegalArgumentException - if zipFile fails Check.validFile; zipApiFile has an invalid extension; directoryExtraction fails DirUtil.ensureExists
SecurityException - if a security manager exists and its SecurityManager.checkRead method denies read access to zipFile or directoryExtraction
IllegalStateException - if directoryExtraction failed to be created or is not an actual directory but is some other type of file
IOException - if an I/O problem occurs

extract

public static void extract(ZipFile zipApiFile,
                           File directoryExtraction,
                           boolean overwrite)
                    throws IllegalArgumentException,
                           SecurityException,
                           IllegalStateException,
                           IOException
Extracts the contents of zipApiFile to directoryExtraction.

Warning: zipApiFile is not closed by this method.

It is an error if zipApiFile does not exist, is not a normal file, or is not in the proper ZIP format. In contrast, directoryExtraction need not exist, since it (and any parent directories) will be created if necessary.

Parameters:
zipApiFile - the ZipFile to get the entries from
directoryExtraction - the directory that will extract the contents of zipApiFile into
overwrite - specifies whether or not extraction is allowed to overwrite an existing normal file inside directoryExtraction
Throws:
IllegalArgumentException - if zipApiFile is null; zipApiFile has an invalid extension; directoryExtraction fails DirUtil.ensureExists
SecurityException - if a security manager exists and its SecurityManager.checkRead method denies read access to zipApiFile or directoryExtraction
IllegalStateException - if directoryExtraction failed to be created or is not an actual directory but is some other type of file
IOException - if an I/O problem occurs

writeOutFile

private static void writeOutFile(ZipFile zipApiFile,
                                 ZipEntry entry,
                                 File path)
                          throws IOException
Writes all the bytes from zipApiFile's current entry to path.

Throws:
IOException - if an I/O problem occurs