/*
* Copyright (c) 2007 by Michael Kiefte
*
* 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.imports;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import VASSAL.build.Buildable;
import VASSAL.build.GameModule;
import VASSAL.build.module.Map;
import VASSAL.build.module.map.LayeredPieceCollection;
import VASSAL.build.widget.PieceSlot;
import VASSAL.tools.ArchiveWriter;
/**
* Abstract parent of all importer classes.
*
* @author Michael Kiefte
* @since 3.1.0
*/
public abstract class Importer {
/*
* We need this for the getCaseInsensitiveFile method.
*/
protected ImportAction action;
protected File file;
static protected void insertComponent(Buildable child, Buildable parent) {
child.build(null);
if (child instanceof PieceSlot)
((PieceSlot) child).updateGpId(GameModule.getGameModule());
child.addTo(parent);
parent.add(child);
}
/**
* @return The VASSAL Map object corresponding to this imported map.
*/
protected Map getMainMap() {
return GameModule.getGameModule().getAllDescendantComponentsOf(Map.class).iterator().next();
}
protected LayeredPieceCollection getLayeredPieceCollection() {
final Map map = getMainMap();
final List<LayeredPieceCollection> l =
map.getComponentsOf(LayeredPieceCollection.class);
LayeredPieceCollection collection = null;
if (l.size() == 0) {
collection = new LayeredPieceCollection();
insertComponent(collection, map);
collection.setAttribute(LayeredPieceCollection.PROPERTY_NAME, "Layer");
}
else {
assert(l.size() == 1);
collection = l.get(0);
}
return collection;
}
/**
* The method that actually loads the file and creates the classes containing information needed for the archive.
* The archive is written in <code>writeToArchive</code>.
*
* @param f The base file to be imported.
* @throws IOException
*/
protected void load(File f) throws IOException {
this.file = f;
}
/**
* Create the VASSAL module based on the classes created by <code>load</code>. This should not be called directly
* but rather <code>importFile</code>.
*
* @throws IOException
*/
public abstract void writeToArchive() throws IOException;
/**
* Two methods are needed to import a file. <code>Importer.importFile</code> initializes the game module
* and calls <code>load</code> which must be overridden by descendents.
*
* @param action <code>ImportAction</code> which creates the <code>Importer</code>. This is needed for
* file dialogs that may be called by <code>Importer</code> methods.
* @param f The base file to be imported.
* @throws IOException
*/
public void importFile(ImportAction action, File f) throws IOException {
this.action = action;
load(f);
}
/**
* Return a file name without the extension.
*/
public static String stripExtension(String s) {
if (s.equals(".") || s.equals(".."))
return s;
final int index = s.lastIndexOf('.');
final int pathIdx = s.lastIndexOf(File.separatorChar);
if (index == -1 || index < pathIdx)
return s;
else
return s.substring(0, index);
}
/**
* Read a null-terminated string representing a Windows file name and convert
* Windows separator characters <tt>'\\'</tt> to the local separator character.
* This is the default file name format for many imported modules and should be used
* whenever a filename is read as a null-terminated string in order to ensure
* platform independence.
*/
public static String readWindowsFileName(InputStream in) throws IOException {
final StringBuilder sb = new StringBuilder();
char ch;
do {
ch = (char) in.read();
if (ch == '\\')
sb.append(File.separatorChar);
else if (ch != 0)
sb.append(ch);
} while (ch != 0);
return sb.toString();
}
/**
* Read a null-terminated string from a file up to a maximum length which includes
* the null termination. If the actual string is longer, no more bytes will be read.
*/
public static String readNullTerminatedString(InputStream in, int maxLen) throws IOException {
final StringBuilder sb;
if (maxLen == 0)
sb = new StringBuilder();
else
sb = new StringBuilder(maxLen);
char ch;
for (int i = 0; maxLen == 0 || i < maxLen; ++i) {
ch = (char) in.read();
if (ch >= 0x20 && ch <= 0x7e)
sb.append(ch);
else if (ch != 0)
sb.append(" ");
else
break;
}
return sb.toString();
}
/**
* Return a null-terminated string from an input stream.
*/
public static String readNullTerminatedString(InputStream in) throws IOException {
return Importer.readNullTerminatedString(in, 0);
}
/**
* Get a unique file name for an image in the game module archive. This function
* tests the provided name against those that are already present in the archive
* and if the file name already exists, creates an alternate, unique file name.
* If an alternate is created, it is of the form <code><tt>name</tt> + "(<tt>n</tt>)"</code>
* where <tt>n</tt> is an integer.
*/
public static String getUniqueImageFileName(String s, String ext) {
String t = s;
int index = 0;
final ArchiveWriter writer = GameModule.getGameModule().getArchiveWriter();
try {
while (writer.contains(writer.getImagePrefix() + t + ext))
t = s + '(' + (++index) + ')';
}
catch (IOException e) {
// FIXME: ????
}
return t + ext;
}
public static String getUniqueImageFileName(String s) {
return getUniqueImageFileName(s, ".png");
}
/**
* Get the file name without the qualifying path.
*/
public static String getFileName(String s) {
if (s.equals(".") || s.equals(".."))
return s;
final int pathIdx = s.lastIndexOf(File.separatorChar);
return s.substring(pathIdx + 1, s.length());
}
/**
* Get the extension from a file name.
*/
public static String getExtension(String s) {
if (s.equals(".") || s.equals(".."))
return "";
final int extIdx = s.lastIndexOf('.');
final int pathIdx = s.lastIndexOf(File.separatorChar);
if (extIdx == -1 || extIdx < pathIdx || extIdx >= s.length() - 1)
return "";
else
return s.substring(extIdx + 1);
}
/**
* Strip the extension from a filename and replace with the given extension.
*/
public static String forceExtension(String s, String ext) {
if (s.equals(".") || s.equals(".."))
return s;
return Importer.stripExtension(s) + '.' + ext;
}
/**
* Determine whether the file is valid for the given importer.
* @param f
* @return <code>true</code> if <code>f</code> is a valid file for this type.
* @throws FileNotFoundException
* @throws IOException
*/
public abstract boolean isValidImportFile(File f) throws IOException;
}