/*
* $Id$
*
* Copyright (c) 2000-2008 by Rodney Kinney, Joel Uckelman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.tools;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.zip.ZipFile;
import VASSAL.build.GameModule;
import VASSAL.configure.DirectoryConfigurer;
import VASSAL.launch.Launcher;
import VASSAL.preferences.Prefs;
import VASSAL.tools.filechooser.FileChooser;
import VASSAL.tools.image.svg.SVGImageUtils;
import VASSAL.tools.imageop.Op;
import VASSAL.tools.io.FileArchive;
import VASSAL.tools.io.IOUtils;
import VASSAL.tools.io.ZipArchive;
/**
* An ArchiveWriter is a writeable DataArchive. New files may be added
* with the {@link #addFile} and {@link #addImage} methods.
*/
public class ArchiveWriter extends DataArchive {
private String archiveName;
private boolean isTempArchive = false;
/**
* Create a new writeable archive.
*
* @param zipName the name of the archive. If null, the user will be
* prompted for a filename when saving. If not null, new entries will
* be added to the named archive. If the file exists and is not a zip
* archive, it will be overwritten.
*/
public ArchiveWriter(String zipName) {
archiveName = zipName;
if (archiveName == null) {
isTempArchive = true;
try {
archiveName = File.createTempFile("tmp", ".zip").getPath();
}
catch (IOException e) {
WriteErrorDialog.error(e, archiveName);
}
}
final File f = new File(archiveName);
try {
if (f.exists()) {
try {
archive = new ZipArchive(archiveName);
}
catch (IOException e1) {
// the file is not a valid ZIP archive, truncate it
archive = new ZipArchive(archiveName, true);
}
}
else {
archive = new ZipArchive(archiveName);
}
}
catch (IOException e) {
archive = null;
WriteErrorDialog.error(e, archiveName);
}
}
public ArchiveWriter(FileArchive archive) {
archiveName = archive.getName();
this.archive = archive;
}
@Deprecated
public ArchiveWriter(ZipFile archive) {
archiveName = archive.getName();
try {
this.archive = new ZipArchive(archiveName);
}
catch (IOException e) {
archive = null;
WriteErrorDialog.error(e, archiveName);
}
}
/**
* Add an image file to the archive. The file will be copied into an
* "images" directory in the archive. Storing another image with the
* same name will overwrite the previous image.
*
* @param path the full path of the image file on the user's filesystem
* @param name the name under which to store the image in the archive
*/
public void addImage(String path, String name) {
// check SVG for external references and pull them in
if (name.toLowerCase().endsWith(".svg")) {
List<String> exrefs = null;
try {
exrefs = SVGImageUtils.getExternalReferences(path);
}
catch (IOException e) {
ReadErrorDialog.error(e, name);
return;
}
for (String s : exrefs) {
final File f = new File(s);
byte[] buf = null;
try {
buf = SVGImageUtils.relativizeExternalReferences(s);
}
catch (IOException e) {
ReadErrorDialog.error(e, f);
continue;
}
addFile(imageDir + f.getName(), buf);
}
}
// otherwise just add what we were given
else {
addFile(path, imageDir + name);
}
Op.load(name).update();
localImages = null;
}
public void addImage(String name, byte[] contents) {
addFile(imageDir + name, contents);
localImages = null;
}
public void addSound(String path, String fileName) {
addFile(path, soundDir + fileName);
}
@Deprecated
public boolean isImageAdded(String name) {
try {
return archive.contains(imageDir + name);
}
catch (IOException e) {
return false;
}
}
public void removeImage(String name) {
try {
archive.remove(imageDir + name);
}
catch (IOException e) {
WriteErrorDialog.error(e, archive.getName());
}
localImages = null;
}
/**
* Copy a file from the user's filesystem to the archive.
*
* @param path the full path of the file on the user's filesystem
* @param fileName the name under which to store the file in the archive
*/
public void addFile(String path, String fileName) {
try {
archive.add(fileName, path);
}
catch (IOException e) {
WriteErrorDialog.error(e, archive.getName());
}
}
/**
* Copy an <code>InputStream</code> into the archive
*
* @param fileName the name under which to store the contents of the stream
* @param in the stream to copy
*/
public void addFile(String fileName, InputStream in) {
OutputStream out = null;
try {
out = archive.getOutputStream(fileName);
IOUtils.copy(in, out);
out.close();
}
catch (IOException e) {
WriteErrorDialog.error(e, archive.getName());
}
finally {
IOUtils.closeQuietly(out);
}
}
public void addFile(String fileName, byte[] content) {
try {
archive.add(fileName, content);
}
catch (IOException e) {
WriteErrorDialog.error(e, archive.getName());
}
}
public void save() throws IOException {
save(false);
}
public void save(boolean notifyModuleManager) throws IOException {
if (isTempArchive) saveAs(notifyModuleManager);
else write(archive, notifyModuleManager);
}
public void saveAs() throws IOException {
saveAs(false);
}
protected void write(FileArchive fa, boolean notifyModuleManager)
throws IOException {
fa.flush();
// FIXME: use a listener here?
if (notifyModuleManager) {
Launcher.getInstance().sendSaveCmd(fa.getFile());
}
}
public void saveAs(boolean notifyModuleManager) throws IOException {
final FileChooser fc = FileChooser.createFileChooser(
GameModule.getGameModule().getFrame(),
(DirectoryConfigurer) Prefs.getGlobalPrefs()
.getOption(Prefs.MODULES_DIR_KEY));
if (fc.showSaveDialog() != FileChooser.APPROVE_OPTION) return;
final String filename = fc.getSelectedFile().getPath();
if (filename != archive.getName()) {
// Copy the current state to the new archive.
final FileArchive tmp = archive;
archive = new ZipArchive(tmp, filename);
archiveName = filename;
archive.flush();
tmp.revert();
tmp.close();
write(archive, notifyModuleManager);
if (isTempArchive) {
tmp.getFile().delete();
isTempArchive = false;
}
}
else {
write(archive, notifyModuleManager);
}
}
/**
* If the ArchiveWriter was initialized with non-null file name, then
* write the contents of the archive to the named archive. If it was
* initialized with a null name, prompt the user to select a new file
* into which to write archive.
*/
@Deprecated
public void write() throws IOException {
write(false);
}
@Deprecated
public void write(boolean notifyModuleManager) throws IOException {
save(notifyModuleManager);
}
}