/*==========================================================================*\
| $Id: ArchiveManager.java,v 1.3 2011/05/13 19:42:32 aallowat Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2006-2011 Virginia Tech
|
| This file is part of Web-CAT.
|
| Web-CAT is free software; you can redistribute it and/or modify
| it under the terms of the GNU Affero General Public License as published
| by the Free Software Foundation; either version 3 of the License, or
| (at your option) any later version.
|
| Web-CAT is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU Affero General Public License
| along with Web-CAT; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package org.webcat.archives;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipOutputStream;
import org.webcat.archives.internal.WritableFolderContainer;
import org.webcat.archives.internal.WritableZipContainer;
import org.webcat.core.FileUtilities;
//-------------------------------------------------------------------------
/**
* This class allows clients to register handlers for various archive formats,
* in order to list their contents or unpack them in a type-independent manner.
* <p>
* The archive manager can also handle directories and files similarly:
* <ul>
* <li>
* For directories, the getContents() method recursively lists its
* contents, and unpack() performs a recursive file-copy operation of the
* directory's contents to the destination path.
* </li>
* <li>
* For a file that is not handled by any registered archive handlers,
* getContents() simply returns the name of the file itself, and unpack()
* performs a single file-copy operation of that file to the destination
* path.
* </li>
* </ul>
*
* @author Tony Allevato
* @author Last changed by $Author: aallowat $
* @version $Revision: 1.3 $, $Date: 2011/05/13 19:42:32 $
*/
public class ArchiveManager
{
//~ Constructors ..........................................................
// ----------------------------------------------------------
/*
* Initializes an object of the ArchiveManager class.
*/
private ArchiveManager()
{
archiveHandlers = new ArrayList<IArchiveHandler>();
}
// ----------------------------------------------------------
/**
* Returns the single instance of the ArchiveManager class.
*
* @return A reference to the ArchiveManager singleton.
*/
public static ArchiveManager getInstance()
{
if (instance == null)
{
instance = new ArchiveManager();
}
return instance;
}
//~ Methods ...............................................................
// ----------------------------------------------------------
/**
* Add an archive handler to the manager. Handlers are asked in the order
* they were added whether they can handle a given archive.
*
* @param handler An archive handler object that implements the
* IArchiveHandler interface.
*/
public void addHandler(IArchiveHandler handler)
{
archiveHandlers.add(handler);
}
// ----------------------------------------------------------
/**
* Gets the contents of the archive pointed to by the specified File. No
* guarantees are made as to whether an IArchiveEntry for a directory in
* the archive will be returned before its children; this behavior is
* dependent on the underlying archive handler.
*
* @param file A File object representing the archive.
*
* @return An array of IArchiveEntry objects describing the contents of
* the archive.
*
* @throws IOException
*/
public IArchiveEntry[] getContents(File file)
throws IOException
{
IArchiveHandler handler = findHandler(file.getName());
if (handler != null)
{
return handler.getContents(file);
}
else
{
if (file.isDirectory())
{
ArrayList<IArchiveEntry> entryList =
new ArrayList<IArchiveEntry>();
getDirectoryContents(file, file, entryList);
IArchiveEntry[] entryArray =
new IArchiveEntry[entryList.size()];
entryList.toArray(entryArray);
return entryArray;
}
else
{
return new IArchiveEntry[] {
new ArchiveEntry(file.getName(), file.isDirectory(),
new Date(file.lastModified()), file.length())
};
}
}
}
// ----------------------------------------------------------
/**
* Gets the contents of the archive to be read from the specified
* InputStream. A filename must be associated with the stream in order to
* properly detect the type of archive being passed. No guarantees are
* made as to whether an IArchiveEntry for a directory in the archive will
* be returned before its children; this behavior is dependent on the
* underlying archive handler.
*
* @param name The name of the archive being passed.
* @param stream An InputStream from which the archive will be read.
*
* @return An array of IArchiveEntry objects describing the contents of
* the archive.
*
* @throws IOException
*/
public IArchiveEntry[] getContents(String name, InputStream stream)
throws IOException
{
return getContents(name, stream, -1);
}
// ----------------------------------------------------------
/**
* Gets the contents of the archive to be read from the specified
* InputStream. A filename must be associated with the stream in order to
* properly detect the type of archive being passed. No guarantees are
* made as to whether an IArchiveEntry for a directory in the archive will
* be returned before its children; this behavior is dependent on the
* underlying archive handler.
*
* @param name The name of the archive being passed.
* @param stream An InputStream from which the archive will be read.
* @param size The size of the input stream, if known.
*
* @return An array of IArchiveEntry objects describing the contents of
* the archive.
*
* @throws IOException
*/
public IArchiveEntry[] getContents(String name,
InputStream stream,
long size)
throws IOException
{
IArchiveHandler handler = findHandler(name);
if (handler != null)
{
return handler.getContents(stream);
}
else
{
return new IArchiveEntry[] {
new ArchiveEntry(name, false, new Date(), size)
};
}
}
// ----------------------------------------------------------
/**
* Unpacks an archive to the specified destination directory. The
* destination directory must already exist; any nested directories
* in the archive will be created as necessary.
*
* @param destPath A File object representing the directory to which the
* archive will be unpacked.
* @param archiveFile A File object representing the archive to be
* unpacked.
*
* @throws IOException
*/
public void unpack(File destPath, File archiveFile)
throws IOException
{
IArchiveHandler handler = findHandler(archiveFile.getName());
if (handler != null)
{
handler.unpack(destPath, archiveFile);
}
else
{
if (archiveFile.isDirectory())
{
FileUtilities.copyDirectoryContents(archiveFile, destPath);
}
else
{
File destFile = new File(destPath, archiveFile.getName());
FileUtilities.copyFileToFile(archiveFile, destFile);
}
}
}
// ----------------------------------------------------------
/**
* Unpacks an archive to the specified destination directory. The
* destination directory must already exist; any nested directories
* in the archive will be created as necessary.
*
* @param destPath A File object representing the directory to which the
* archive will be unpacked.
* @param name The name of the archive being passed.
* @param stream An InputStream from which the archive will be read.
*
* @throws IOException
*/
public void unpack(File destPath, String name, InputStream stream)
throws IOException
{
IArchiveHandler handler = findHandler(name);
if (handler != null)
{
handler.unpack(destPath, stream);
}
else
{
File destFile = new File(destPath, name);
FileUtilities.copyStreamToFile(stream, destFile);
}
}
// ----------------------------------------------------------
public IWritableContainer writableContainerForZip(
ZipOutputStream zipStream)
{
return new WritableZipContainer(zipStream);
}
// ----------------------------------------------------------
public IWritableContainer createWritableContainer(
File path, boolean isArchive)
{
if (isArchive)
{
try
{
return new WritableZipContainer(path);
}
catch (IOException e)
{
return null;
}
}
else
{
return new WritableFolderContainer(path);
}
}
// ----------------------------------------------------------
/*
* Finds the first archive handler that will accept a file with the
* specified name. If no handler currently registered will accept it,
* this function returns null.
*/
private IArchiveHandler findHandler(String name)
{
for (int i = 0; i < archiveHandlers.size(); i++)
{
IArchiveHandler handler = archiveHandlers.get(i);
if (handler.acceptsFile(name))
{
return handler;
}
}
return null;
}
// ----------------------------------------------------------
/*
* Recursively drills down into a directory and populates a List with
* its IArchiveEntry objects representing its contents.
*/
private void getDirectoryContents(
File root, File dir, List<IArchiveEntry> entryList)
{
File[] children = dir.listFiles();
for (int i = 0; i < children.length; i++)
{
File file = children[i];
String rootPath = root.getPath();
String remPath = file.getPath().substring(rootPath.length());
if (remPath.startsWith(File.separator))
{
remPath = remPath.substring(1);
}
entryList.add(new ArchiveEntry(remPath, file.isDirectory(),
new Date(file.lastModified()), file.length()));
if (file.isDirectory())
{
getDirectoryContents(root, file, entryList);
}
}
}
//~ Instance/static variables .............................................
/** The single instance of the ArchiveManager class. */
private static ArchiveManager instance;
/** A list of archive handlers currently registered in the manager. */
private List<IArchiveHandler> archiveHandlers;
}