Index: branches/ant-install-branch/gsdl3/src/java/org/greenstone/core/util/StaticStrings.java
===================================================================
--- branches/ant-install-branch/gsdl3/src/java/org/greenstone/core/util/StaticStrings.java	(revision 9974)
+++ branches/ant-install-branch/gsdl3/src/java/org/greenstone/core/util/StaticStrings.java	(revision 9974)
@@ -0,0 +1,248 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.core.util;
+
+
+/** Were you to guess that this is a class object choc-a-block full of static stringy goodness, you'd be right. They come in several flavours: Those ending _STR are strings you might find as values in XML, _ELEMENT are element names, _ATTRIBUTE are attribute names, _CHAR are particular characters while _CHARACTER are also characters but expressed as strings (for regex purposes ie startsWith, endsWith, indexOf and replaceAll); _PATTERN are strings which require extra escaping to put them through regex. Finally anything else is just a static string used within GLI. */
+
+public class StaticStrings {
+    static final public char   CLOSEBRACKET_CHAR                          = ']';
+    static final public char   DOUBLEQUOTE_CHAR                           = '\"';
+    static final public char   BACKSLASH_CHAR                             = '\\';
+    static final public char   FORWARDSLASH_CHAR                          = '/';
+    static final public char   NEW_LINE_CHAR                              = '\n';
+    static final public char   NS_SEP                                     = '.';
+    static final public char   OPENBRACKET_CHAR                           = '[';
+    static final public char   PIPE_CHAR                                  = '|';
+    static final public char   SINGLEQUOTE_CHAR                           = '\'';
+    static final public char   SPACE_CHAR                                 = ' ';
+    static final public char   STAR_CHAR                                  = '*';
+    static final public String ALLFIELDS_STR                              = "allfields";
+    static final public String ARCPLUG_STR                                = "ArcPlug";
+    static final public String ASSIGNED_ATTRIBUTE                         = "assigned";
+    static final public String ASSOCIATIONS_ELEMENT                       = "Associations";
+    static final public String AT_CHARACTER                               = "@";
+    static final public String ATTRIBUTE_ELEMENT                          = "Attribute";
+    static final public String BUILDTYPE_STR                              = "buildtype";
+    static final public String CACHE_FOLDER                               = "cache";
+    static final public String CCS_STR                                    = "ccs";
+    static final public String CLASSES_FOLDER                             = "classes";
+    static final public String CLASSIFY_ELEMENT                           = "Classify";
+    static final public String CLASSIFY_STR                               = "classify";
+    static final public String CLOSE_PARENTHESIS_CHARACTER                = ")";
+    static final public String CODE_ATTRIBUTE                             = "code";
+    static final public String COLNAME_PATTERN                            = "<col_name>";
+    static final public String COLLECT_CFG                                = "collect.cfg";
+    static final public String COLLECTCONFIGURATION_XML                   = "CollectionConfig.xml";
+    static final public String COLLECTDIR_ARGUMENT                        = "-collectdir";
+    static final public String COLLECTION_ELEMENT                         = "Collection";
+    static final public String COLLECTION_STR                             = "Collection: ";
+    static final public String COLLECTIONMETADATA_BETA_ELEMENT            = "Beta";
+    static final public String COLLECTIONMETADATA_BETA_STR                = "beta";
+    static final public String COLLECTIONMETADATA_COLLECTIONNAME_STR      = "collectionname";
+    static final public String COLLECTIONMETADATA_COLLECTIONEXTRA_STR     = "collectionextra";
+    static final public String COLLECTIONMETADATA_CREATOR_ELEMENT         = "Creator";
+    static final public String COLLECTIONMETADATA_CREATOR_STR             = "creator";
+    static final public String COLLECTIONMETADATA_ELEMENT                 = "CollectionMetadata";
+    static final public String COLLECTIONMETADATA_ICONCOLLECTION_STR      = "iconcollection";
+    static final public String COLLECTIONMETADATA_ICONCOLLECTIONSMALL_STR = "iconcollectionsmall";
+    static final public String COLLECTIONMETADATA_MAINTAINER_ELEMENT      = "Maintainer";
+    static final public String COLLECTIONMETADATA_MAINTAINER_STR          = "maintainer";
+    static final public String COLLECTIONMETADATA_PUBLIC_ELEMENT          = "Public";
+    static final public String COLLECTIONMETADATA_PUBLIC_STR              = "public";
+    static final public String COLLECTIONMETADATA_STR                     = "collectionmeta";
+    static final public String COLON_CHARACTER                            = ":";
+    static final public String COMMA_CHARACTER                            = ",";
+    static final public String COMMENT_VALUE                              = "comment";
+    static final public String COMMENTED_INDEXES_STR                      = "#indexes";
+    static final public String COMMENTED_INDEX_DEFAULT_STR                = "#defaultindex";
+    static final public String COMMENTED_LEVELS_STR                       = "#levels";
+    static final public String CONTENT_ATTRIBUTE                          = "content";
+    static final public String CONTENT_ELEMENT                            = "content";
+    static final public String CUSTOM_ATTRIBUTE                           = "custom";
+    static final public String DC_ATTRIBUTE                               = "dc";
+    static final public String DEBUG_ARGUMENT                             = "-debug";
+    static final public String DEFINITION_VALUE                           = "definition";
+    static final public String DESCRIPTION_ELEMENT                        = "Description";
+    static final public String DICTIONARY_FILENAME                        = "dictionary";
+    static final public String DIRECTORY_MAPPINGS_ELEMENT                 = "DirectoryMappings";
+    static final public String DOCUMENT_STR                               = "document";
+    static final public String ETC_FOLDER                                 = "etc";
+    static final public String EMPTY_STR                                  = "";
+    static final public String ENGLISH_LANGUAGE_STR                       = "en";
+    static final public String ENTRY_ELEMENT                              = "Entry";
+    static final public String ENUM_STR                                   = "enum";
+    static final public String EQUALS_CHARACTER                           = "=";
+    static final public String ESCAPE_PATTERN                             = "\\\\";
+    static final public String ESCAPE_STR                                 = "\\";
+    static final public String EXCLAMATION_CHARACTER                     = "!";
+    static final public String EXCLUDE_STR                                = "exclude";
+    static final public String EXTENSION_ATTRIBUTE                        = "extension";
+    static final public String EXTRACTED_METADATA_ELEMENT                 = "XMetadata";
+    static final public String EXTRACTED_NAMESPACE                        = "ex.";
+    static final public String FALSE_STR                                  = "false";
+    static final public String FEEDBACK_ARGUMENT                          = "-feedback";
+    static final public String FILE_ATTRIBUTE                             = "file";
+    static final public String FILENAME_STR                               = "Filename";
+    static final public String FLAG_STR                                   = "flag";
+    static final public String FORMAT_ELEMENT                             = "Format";
+    static final public String FORMAT_STR                                 = "format";
+    static final public String GLI_ATTRIBUTE                              = "gli";
+    static final public String GREATER_THAN_CHARACTER                     = ">";
+    static final public String GSDL_ARGUMENT                              = "-gsdl";
+    static final public String GSDL3_ARGUMENT                              = "-gsdl3";
+    static final public String HFILE_ARGUMENT                             = "-hfile";
+    static final public String HIDDENGLI_ELEMENT                          = "HiddenGLI";
+    static final public String HIERARCHY_ATTRIBUTE                        = "hierarchy";
+    static final public String HIERARCHY_CLASSIFIER                       = "Hierarchy";
+    static final public String HIERARCHY_STR                              = "hierarchy";
+    static final public String HTTP_PROTOCOL_STR                          = "http://";
+    static final public String HELP_ARGUMENT                              = "-help";
+    static final public String HELP_INDEX_FILENAME                        = "help_index.xml";
+    static final public String HTM_FILE_EXTENSION                         = ".htm";
+    static final public String IDENTIFIER_VALUE                           = "identifier";
+    static final public String IMAGES_FOLDER                              = "images";
+    static final public String IMAGES_PATH_RELATIVE_TO_COLLECTION_PREFIX  = "_httpcollection_/images/";
+    static final public String IMAGES_PATH_RELATIVE_TO_GSDL_PREFIX        = "_httpprefix_/collect/<col_name>/images/";
+    static final public String IMPORT_FOLDER                              = "import";
+    static final public String INCLUDE_STR                                = "include";
+    static final public String INDEX_ELEMENT                              = "Index";
+    static final public String INDEX_DEFAULT_ELEMENT                      = "DefaultIndex";
+    static final public String INDEX_DEFAULT_STR                          = "defaultindex";
+    static final public String INDEX_STR                                  = "indexes";
+    static final public String INDEXES_ELEMENT                            = "Indexes";
+    static final public String INT_STR                                    = "int";
+    static final public String[] KEEP_PLUG                                = { "GAPlug", "METSPlug" };
+
+    static final public String LANGUAGE_ARGUMENT                          = "l=";
+    static final public String LANGUAGE_ATTRIBUTE                         = "language";
+    static final public String LANGUAGE_ELEMENT                           = "Language";
+    static final public String LANGUAGE_DEFAULT_ELEMENT                   = "DefaultLanguage";
+    static final public String LANGUAGE_DEFAULT_STR                       = "defaultlanguage";
+    static final public String LANGUAGE_STR                               = "language";
+    static final public String LANGUAGEDEPENDENT_ATTRIBUTE                = "language_dependent";
+    static final public String LANGUAGES_ELEMENT                          = "Languages";
+    static final public String LANGUAGES_STR                              = "languages";
+    static final public String LBRACKET_CHARACTER                         = "[";
+    static final public String LBRACKET_PATTERN                           = "\\[";
+    static final public String LESS_THAN_CHARACTER                        = "<";
+    static final public String LEVEL_ATTRIBUTE                            = "level";
+    static final public String LEVELS_ELEMENT                             = "Levels";
+    static final public String LEVELS_STR                                 = "levels";
+    static final public String LIBRARY_URL_ARGUMENT                       = "-library_url";
+    static final public String LOCAL_LIBRARY_ARGUMENT                     = "-local_library";
+    static final public String LOAD_ARGUMENT                              = "-load";
+    static final public String MAC_OPEN_COMMAND                           = "open %1";
+    static final public String MAPPING_ELEMENT                            = "Mapping";
+    static final public String MARC_EXTENSION                             = ".marc";
+    static final public String MDS_ATTRIBUTE                              = "mds";
+    static final public String METADATA_ARGUMENT                          = "-metadata";
+    static final public String METADATA_BAK                               = "~metadata.xml";
+    static final public String METADATA_ELEMENT                           = "Metadata";
+    static final public String METADATA_SET_EXTENSION                     = ".mds";
+    static final public String METADATA_STR                               = "metadata";
+    static final public String METADATA_TYPE_STR                          = "metadata";
+    static final public String METADATA_XML                               = "metadata.xml";
+    static final public String METADATUM_TYPE_STR                         = "metadatum";
+    static final public String MGPP_ATTRIBUTE                             = "mgpp_enabled";
+    static final public String MINUS_CHARACTER                            = "-";
+    static final public String MODE_STR                                   = "Mode: ";
+    static final public String MODEGLI_ELEMENT                            = "ModeGLI";
+    static final public String MODEL_COLLECTION_NAME                      = "modelcol";
+    static final public String NAME_ATTRIBUTE                             = "name";
+    static final public String NAME_ELEMENT                               = "Name";
+    static final public String NEWLINE_CHARACTER                          = "\\";
+    static final public String NEWLINE_ELEMENT                            = "NewLine";
+    static final public String NO_LOAD_ARGUMENT                           = "-no_load";
+    static final public String NO_WGET_STR                                = "NoWGet";
+    static final public String ONE_CHARACTER                              = "1";
+    static final public String OPEN_PARENTHESIS_CHARACTER                 = "(";
+    static final public String OPTION_ELEMENT                             = "Option";
+    static final public String OPTIONS_ATTRIBUTE                          = "options";
+    static final public String PARAGRAPH_STR                              = "paragraph";
+    static final public String PERL_ARGUMENT                              = "-perl";
+    static final public String PHIND_CLASSIFIER                           = "Phind";
+    static final public String PIPE_STR                                   = "|";
+    static final public String PLUGIN_ELEMENT                             = "Plugin";
+    static final public String PLUGIN_STR                                 = "plugin";
+    static final public String PROPERTIES_FILE_EXTENSION                  = ".properties";
+    static final public String PSEUDO_COLLECTCONFIGURATION_XML            = "xml/CollectionConfig.xml";
+    static final public String RANGE_ELEMENT                              = "Range";
+    static final public String RBRACKET_CHARACTER                         = "]";
+    static final public String RBRACKET_PATTERN                           = "\\]";
+    static final public String RECPLUG_STR                                = "RecPlug";
+    static final public String REGEXP_STR                                 = "regexp";
+    static final public String SEARCHTYPE_ELEMENT                         = "SearchType";
+    static final public String SEARCHTYPE_STR                             = "searchtype";
+    static final public String SECTION_ELEMENT                            = "Section";
+    static final public String SECTION_STR                                = "section";
+    static final public String SEPARATOR_ATTRIBUTE                        = "separator";
+    static final public String SEPARATOR_CHARACTER                        = "/";
+    static final public String SERVLET_ARGUMENT                           ="-servlet";
+    static final public String SETLANGUAGE_ELEMENT                        = "SetLanguage";
+    static final public String SHOW_PROGRESS_ARGUMENT                     = "show_progress";
+    static final public String SINGLE_ESCAPE_ONLY_PATTERN                 = "[^\\\\]+\\\\[^\\\\]+";
+    static final public String SINGLE_QUOTE_CHARACTER                     = "'";
+    static final public String SITE_ARGUMENT                              = "-site";
+    static final public String SOURCESEGMENT_VALUE                        = "SourceSegment";
+    static final public String SPACE_CHARACTER                            = " ";
+    static final public String SPECIAL_ATTRIBUTE                          = "special";
+    static final public String SPEECH_CHARACTER                           = "\"";
+    static final public String STOP_CHARACTER                             = ".";
+    static final public String STRING_STR                                 = "string";
+    static final public String SUBCOLLECTION_ELEMENT                      = "Subcollection";
+    static final public String SUBCOLLECTION_STR                          = "subcollection";
+    static final public String SUBCOLLECTION_DEFAULT_INDEX_ELEMENT        = "DefaultSubcollection";
+    static final public String SUBCOLLECTION_DEFAULT_INDEX_STR            = "defaultsubcollection";
+    static final public String SUBCOLLECTION_INDEX_STR                    = "indexsubcollections";
+    static final public String SUBCOLLECTION_INDEXES_ELEMENT              = "SubcollectionIndexes";
+    static final public String SUPERCOLLECTION_ELEMENT                    = "Supercollection";
+    static final public String SUPERCOLLECTION_STR                        = "supercollection";
+    static final public String TAB_CHARACTER                              = "\t";
+    static final public String TEXT_NODE                                  = "#text";
+    static final public String TEXT_STR                                   = "text";
+    static final public String TIMESTAMP_ARGUMENT                         = "&uq=";
+    static final public String TITLE_ELEMENT                              = "Title";
+    static final public String TRUE_STR                                   = "true";
+    static final public String TRUNCATED_STRING                           = "...";
+    static final public String TYPE_ATTRIBUTE                             = "type";
+    static final public String UNDERSCORE_CHARACTER                       = "_";
+    static final public String UNKNOWN_ELEMENT                            = "Unknown";
+    static final public String UNKNOWNPLUG_STR                             = "UnknownPlug";
+    static final public String USE_METADATA_FILES_ARGUMENT                = "use_metadata_files";
+    static final public String URL_SEPARATOR_CHARACTER                    = "/";
+    static final public String VALUE_ATTRIBUTE                            = "value";
+    static final public String VALUE_TREE_ATTRIBUTE                       = "remember_values";
+    static final public String VERSION_ATTRIBUTE                          = "version";
+    static final public String WGET_ARGUMENT                              = "-wget";
+    static final public String WGET_OLD_STR                               = "<WGet1.9";
+    static final public String WGET_STR                                   = "WGet1.9";
+    static final public String WIN_9X_OPEN_COMMAND                        = "command.com /c start \"%1\"";
+    static final public String WIN_OPEN_COMMAND                           = "cmd.exe /c start \"\" \"%1\"";
+    static final public String YES_STR                                    = "yes";
+    static final public String ZERO_CHARACTER                             = "0";
+}
Index: branches/ant-install-branch/gsdl3/src/java/org/greenstone/core/util/Utility.java
===================================================================
--- branches/ant-install-branch/gsdl3/src/java/org/greenstone/core/util/Utility.java	(revision 9974)
+++ branches/ant-install-branch/gsdl3/src/java/org/greenstone/core/util/Utility.java	(revision 9974)
@@ -0,0 +1,1044 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * <BR><BR>
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ * Modified by: Chi-Yu Huang 03/2005
+ * <BR><BR>
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * <BR><BR>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * <BR><BR>
+ *
+ * This program 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.
+ *
+ * <BR><BR>
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.core.util;
+
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.zip.*;
+import javax.swing.*;
+import javax.swing.tree.*;
+import org.apache.xerces.parsers.*;
+import org.apache.xerces.dom.DocumentImpl;
+import org.apache.xml.serialize.*;
+import org.greenstone.core.Configuration;
+import org.greenstone.core.DebugStream;
+import org.greenstone.core.Dictionary;
+//import org.greenstone.core.shell.GShell;
+
+// Don't even think about adding import org.greenstone.gatherer.Gatherer in here!
+// The functions in this class should be independent of the Gatherer class. Put your function somewhere else buster!
+import org.w3c.dom.*;
+import org.xml.sax.*;
+
+/** To provide a library of common methods, in a static context, for use in the Gatherer.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3b
+ */
+public class Utility {
+    static final public Dimension MINIMUM_BUTTON_SIZE = new Dimension(100, 25);
+    // static final public Dimension DOUBLE_IMAGE_BUTTON_SIZE = new Dimension(190, 35);
+    static final public Dimension LABEL_SIZE = new Dimension(150, 25);
+    /** The default size of a gatherer progress bar, in either the download view or the build view. */
+    static final public Dimension PROGRESS_BAR_SIZE = new Dimension(580,65);
+    /** The number of kilobytes to use as a io buffer. */
+    static final public int FACTOR = 1;
+    /** The size of the io buffer, calculated as FACTOR * 1024. */
+    static final public int BUFFER_SIZE = FACTOR * 1024;
+    /** Definition of an important directory name, in this case the base dir, or the working directory of the Gatherer. */
+
+    static public String BASE_DIR  = System.getProperty("user.dir") + File.separator;
+    static final public String BUILD_CFG_FILENAME = "build.cfg";
+    static final public String CFG_COLLECTIONMETA_COLLECTIONNAME = "collectionmeta\tcollectionname";
+    static final public String CFG_COLLECTIONMETA_COLLECTIONEXTRA = "collectionmeta\tcollectionextra";
+    static final public String CFG_COLLECTIONMETA_ICONCOLLECTION = "collectionmeta\ticoncollection";
+    static final public String CFG_CLASSIFY = "classify";
+    static final public String CFG_CLASSIFY_BUTTONNAME = "-buttonname";
+    static final public String CFG_CLASSIFY_HFILE = "-hfile";
+    static final public String CFG_CLASSIFY_METADATA = "-metadata";
+    static final public String CFG_CLASSIFY_SORT = "-sort";
+    static final public String CFG_CREATOR = "creator";
+    static final public String CFG_FORMAT = "format";
+    static final public String CFG_MAINTAINER = "maintainer";
+    /** Definition of an important directory name, in this case the parent directory of all the collections in the gsdl. */
+    static final public String COL_DIR = "collect" + File.separator;
+    static final public String COLLECTION_TREE = "Collection";
+    /** Definition of an important directory name, in this case the file the collection configuration is expect to be in. */
+    static final public String CONFIG_FILE = "etc" + File.separator + "collect.cfg";
+    static final public String EXTRACTED_METADATA_NAMESPACE = "ex";
+    /** The location of the default greenstone metadata file. */
+    static final public String GREENSTONEDIRECTORYMETADATA_TEMPLATE = "xml/metadata.xml";
+    static final public String GLI_ARCHIVE = "GLI.jar";
+    static final public String GLI_EXTENSION = ".col";
+    /** Definition of an important directory name, in this case the images directory for the collection. */
+    static final public String IMAGES_DIR = "images" + File.separator;
+    /** Definition of an important directory name, in this case the import directory for the collection. */
+    static final public String IMPORT_DIR = "import" + File.separator;
+    /** Definition of an important directory name, in this case the backup import directory for the collection. */
+    static final public String IMPORT_BAK_DIR = "import.bak" + File.separator;
+    /** Definition of an important directory name, in this case the log directory for the collection. */
+    static final public String LOG_DIR = "log" + File.separator;
+    /** Definition of an important directory name, in this case the macros directory for the collection. */
+    static final public String MACROS_DIR = "macros" + File.separator;
+    /** Definition of an important directory name, in this case the location of the expected collection metadata sets.. */
+    static final public String META_DIR = "metadata" + File.separator; // Col. Copy
+    /** Definition of an important directory name, in this case the location of the default metadata sets. */
+    static public String METADATA_DIR = BASE_DIR + "metadata" + File.separator;
+    /** Definition of an important zip file, in this case zipped up version of metadata file stored in JAR file */
+    static final public String METADATA_ZIP = "metadata.zip";
+    /** The default name of the perl executable under unix. */
+    static final public String PERL_EXECUTABLE_UNIX = "perl";
+    /** The default name of the perl executable under windows. */
+    static final public String PERL_EXECUTABLE_WINDOWS = "Perl.exe";
+    /** The name of the Gatherer. */
+    static final public String PROGRAM_NAME = "Greenstone Librarian Interface";
+    /** The current version of the Gatherer. */
+    static final public String PROGRAM_VERSION = "v2.53";
+    static final public String WORKSPACE_TREE = "Workspace";
+    static final public String XML_DIRECTORY = "xml" + File.separator;
+
+    // These are out of alphabetic order to avoid forward reference error.
+    /** The default icon to produce a 'help-icon' sized blank space before a menu entry. */
+    static public Class base = null;
+    static public ImageIcon BLANK_ICON = null;
+    /** The default error icon image. */
+    static public ImageIcon ERROR_ICON = null; 
+    static public ImageIcon HELP_ICON = null; 
+	
+
+    /** It turns out that in Java you have to make sure a directory is empty before you delete it (much like unix I suppose), and so just like unix I'll have to set up a recursive delete function.
+     * @param file The <strong>File</strong> you want to delete.
+     * @return A <i>boolean</i> which is <i>true</i> if the file specified was successfully deleted, <i>false</i> otherwise.
+     */
+    static public boolean delete(File file)
+    {
+	// Nothing to do if it doesn't exist
+	if (!file.exists()) {
+	    return true;
+	}
+
+	// If file is a directory, we have to recursively delete its contents first
+	if (file.isDirectory()) {
+	    File files[] = file.listFiles();
+	    for (int i = 0; i < files.length; i++) {
+		if (delete(files[i]) == false) {
+		    System.err.println("Error: Could not delete folder " + file);
+		    return false;
+		}
+	    }
+	}
+
+	// Delete file
+	if (file.delete() == false) {
+	    System.err.println("Error: Could not delete file " + file);
+	    return false;
+	}
+
+	return true;
+    }
+
+
+    static public boolean delete(String filename) {
+	return delete(new File(filename));
+    }
+
+
+    /** Convert a long, detailing the length of a file in bytes, into a nice human readable string using b, kb, Mb and Gb. */
+    static final public String BYTE_SUFFIX = " b";
+    static final public long GIGABYTE = 1024000000l;
+    static final public String GIGABYTE_SUFFIX = " Gb";
+    static final public long KILOBYTE = 1024l;
+    static final public String KILOBYTE_SUFFIX = " kb";
+    static final public long MEGABYTE = 1024000l;
+    static final public String MEGABYTE_SUFFIX = " mb";
+    static final public String formatFileLength(long length) {
+	StringBuffer result = new StringBuffer("");
+	float number = 0f;
+	String suffix = null;
+	// Determine the floating point number and the suffix (radix) used.
+	if(length >= GIGABYTE) {
+	    number = (float) length / (float) GIGABYTE;
+	    suffix = GIGABYTE_SUFFIX;
+	}
+	else if(length >= MEGABYTE) {
+	    number = (float) length / (float) MEGABYTE;
+	    suffix = MEGABYTE_SUFFIX;
+	}
+	else if(length >= KILOBYTE) {
+	    number = (float) length / (float) KILOBYTE;
+	    suffix = KILOBYTE_SUFFIX;
+	}
+	else {
+	    // Don't need to do anything fancy if the file is smaller than a kilobyte
+	    return length + BYTE_SUFFIX;
+	}
+	// Create the formatted string remembering to round the number to 2.d.p. To do this copy everything in the number string from the start to the first occurance of '.' then copy two more digits. Finally search for and print anything that appears after (and including) the optional 'E' delimter.
+	String number_str = Float.toString(number);
+	char number_char[] = number_str.toCharArray();
+	int pos = 0;
+	// Print the characters up to the '.'
+	while(number_char != null && pos < number_char.length && number_char[pos] != '.') {
+	    result.append(number_char[pos]);
+	    pos++;
+	}
+	if(pos < number_char.length) {
+	    // Print the '.' and at most two characters after it
+	    result.append(number_char[pos]);
+	    pos++;
+	    for(int i = 0; i < 2 && pos < number_char.length; i++, pos++) {
+		result.append(number_char[pos]);
+	    }
+	    // Search through the remaining string for 'E'
+	    while(pos < number_char.length && number_char[pos] != 'E') {
+		pos++;
+	    }
+	    // If we still have string then we found an E. Copy the remaining string.
+	    while(pos < number_char.length) {
+		result.append(number_char[pos]);
+		pos++;
+	    }
+	}
+	// Add suffix
+	result.append(suffix);
+	// Done
+	return result.toString();
+    }
+
+    /** This method formats a given string, using HTML markup, so its width does not exceed the given width 
+     * and its appearance if justified.
+     * @param text The <strong>String</strong> requiring formatting.
+     * @param width The maximum width per line as an <i>int</i>.
+     * @return A <strong>String</strong> formatted so as to have no line longer than the specified width.
+     * TODO Currently HTML formatting tags are simply removed from the text, as the effects of spreading HTML tags over a break are undetermined. To solve this we need to associate tags with a certain text token so if it gets broken on to the next line the tags go with it, or if the tags cover a sequence of words that are broken we need to close then reopen the tags. However all this is a major task and well beyond anything I have time to 'muck-round' on.
+     */
+    /*static public String formatHTMLWidth(String text, int width) {
+	if(text == null) {
+	    return "Error";
+	}
+	HTMLStringTokenizer html = new HTMLStringTokenizer(text);
+	int current_width = 0;
+	int threshold = width / 2;
+	Stack lines = new Stack();
+	String line = "";
+	while(html.hasMoreTokens()) {
+	    String token = html.nextToken();
+	    while(token != null) {
+		if(html.isTag()) {
+		    // Insert smart HTML tag code here.
+		    token = null;
+		}
+		else {
+		    // If the token is bigger than two thirds width, before we've even started break it down.
+		    if(current_width + 1 + token.length() > width && token.length() > threshold) {
+			if(width == current_width) {
+			    lines.push(line);
+			    line = token;
+			    current_width = token.length();
+			}
+			else {
+			    String prefix = token.substring(0, width - 1 - current_width);
+			    token = token.substring(prefix.length());
+			    if(current_width == 0) {
+				line = line + prefix;
+			    }
+			    else {
+				line = line + " " + prefix;
+			    }
+			    lines.push(line);
+			    line = "";
+			    current_width = 0;
+			}
+		    }
+		    // If adding the next token would push us over the maximum line width.
+		    else if(current_width + 1 + token.length() > width) {
+			lines.push(line);
+			line = token;
+			current_width = token.length();
+			token = null;
+		    }
+		    // Otherwise we should be able to just add the token, give or take.
+		    else {
+			if(current_width == 0) {
+			    line = line + token;
+			    current_width = token.length();
+			}
+			else {
+			    // Special case for standard punctuation which may exist after a tag like so:
+			    // My name is <scratchy>Slim Shady</scratchy>.   <-- Annoying punctuation.
+			    if(token.equals(".") || token.equals(",") || token.equals("!") || token.equals("?")) {
+				line = line + token;
+				current_width = current_width + 1;
+			    }
+			    else {
+				line = line + " " + token;
+				current_width = current_width + 1 + token.length();
+			    }
+			}
+			token = null;
+		    }
+		}
+	    }
+	}
+	String result = line;
+	while(!lines.empty()) {
+	    result = (String)lines.pop() + "<BR>" + result;
+	}
+	// Replace ' ' with "&nbsp;"
+	boolean tag = false;
+	int pos = 0;
+	while(pos < result.length()) {
+	    if(result.charAt(pos) == '<') {
+		tag = true;
+	    }
+	    else if(result.charAt(pos) == '>') {
+		tag = false;
+	    }
+	    else if(result.charAt(pos) == ' ' && !tag) {
+		String prefix = result.substring(0, pos);
+		String suffix = result.substring(pos + 1);
+		result = prefix + "&nbsp;" + suffix;
+	    }
+	    pos++;
+	}
+	result = "<HTML>" + result + "</HTML>";
+	return result;
+	}*/
+    /** Format the given filename path string so that it is no longer than the given width. If it is wider replace starting directories with ...
+     * @param key The key <strong>String</Strong> used to retrieve a phrase from the dictionary for this item.
+     * @param raw The raw filename path <strong>String</strong>.
+     * @param width The maximum width as an <i>int</i>.
+     * @return A path <strong>String</strong> no longer than width.
+     */
+    static public String formatPath(String key, String raw, int width) {
+	JLabel label = new JLabel(Dictionary.get(key, raw));
+	int position = -1;
+	while(label.getPreferredSize().width > width && (position = raw.indexOf(File.separator)) != -1) {
+	    raw = "..." + raw.substring(position + 1);
+	    label.setText(Dictionary.get(key, raw));
+	}
+	if(raw.indexOf(File.separator) == -1 && raw.startsWith("...")) {
+	    raw = raw.substring(3);
+	}
+	return raw;
+    }
+
+
+    /** Builds the cache dir by appending the user path and 'cache'.
+     * @return a File representing the path to the private file cache within the current collection.
+     */
+    public static File getCacheDir() {
+	return new File(getGLIUserFolder(), StaticStrings.CACHE_FOLDER);
+    }
+
+    
+    /** Method which constructs the configuration file given a certain collection.
+     * @param col_dir The location of the collection directory as a <strong>String</strong>.
+     * @return The location of the given collections configuration file, also as a <strong>String</strong>.
+     */
+    static public String getConfigFile(String col_dir) {
+	return col_dir + CONFIG_FILE;
+    }
+
+    static public String getDateString() {
+	Calendar current = Calendar.getInstance();
+	String day_name = null;
+	switch(current.get(Calendar.DAY_OF_WEEK)) {
+	case Calendar.MONDAY: day_name = "Dates.Mon"; break;
+	case Calendar.TUESDAY: day_name = "Dates.Tue"; break;
+	case Calendar.WEDNESDAY: day_name = "Dates.Wed"; break;
+	case Calendar.THURSDAY: day_name = "Dates.Thu"; break;
+	case Calendar.FRIDAY: day_name = "Dates.Fri"; break;
+	case Calendar.SATURDAY: day_name = "Dates.Sat"; break;
+	case Calendar.SUNDAY: day_name = "Dates.Sun"; break;
+	default: day_name = "";
+	}
+	String month_name = null;
+	switch(current.get(Calendar.MONTH)) {
+	case Calendar.JANUARY: month_name = "Dates.Jan"; break;
+	case Calendar.FEBRUARY: month_name = "Dates.Feb"; break;
+	case Calendar.MARCH: month_name = "Dates.Mar"; break;
+	case Calendar.APRIL: month_name = "Dates.Apr"; break;
+	case Calendar.MAY: month_name = "Dates.May"; break;
+	case Calendar.JUNE: month_name = "Dates.Jun"; break;
+	case Calendar.JULY: month_name = "Dates.Jul"; break;
+	case Calendar.AUGUST: month_name = "Dates.Aug"; break;
+	case Calendar.SEPTEMBER: month_name = "Dates.Sep"; break;
+	case Calendar.OCTOBER: month_name = "Dates.Oct"; break;
+	case Calendar.NOVEMBER: month_name = "Dates.Nov"; break;
+	case Calendar.DECEMBER: month_name = "Dates.Dec"; break;
+	default: month_name = "";
+	}
+	int day = current.get(Calendar.DAY_OF_MONTH);
+	int hour = current.get(Calendar.HOUR_OF_DAY);
+	int minute = current.get(Calendar.MINUTE);
+	int second = current.get(Calendar.SECOND);
+	int year = current.get(Calendar.YEAR);
+
+	return Dictionary.get(day_name) + " " + Dictionary.get(month_name) + " " + day + " " + year + " " + Utility.pad(String.valueOf(hour), 2, '0', true) + ":" + Utility.pad(String.valueOf(minute), 2, '0', true) + ":" + Utility.pad(String.valueOf(second), 2, '0', true);
+    }
+
+
+    static final private String APPLICATION_DATA_FOLDER = "Application Data";
+    static final private String UNIX_GLI_CONFIG_FOLDER = ".gli";
+    static final private String USER_HOME_PROPERTY = "user.home";
+    static final private String WIN_GLI_CONFIG_FOLDER = "Greenstone" + File.separator + "GLI";
+
+    static public File getGLIUserFolder()
+    {
+	if (Utility.isWindows()) {
+	    return new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + APPLICATION_DATA_FOLDER + File.separator + WIN_GLI_CONFIG_FOLDER + File.separator);
+	}
+	else {
+	    return new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + UNIX_GLI_CONFIG_FOLDER + File.separator);
+	}
+    }
+
+
+    /** Retrieve the full file path to the help index xml file.
+     * @return the full path as a String
+     */
+    static public String getHelpFolder() {
+
+	String help_folder = "help/" + Configuration.getLanguage() + "/";
+
+	// Try in the JAR/classes directory first
+	URL help_folder_url = base.getResource("/" + help_folder);
+	if (help_folder_url != null) {
+	    return help_folder;
+	}
+
+	// Look in the base directory next
+	File help_folder_file = new File(help_folder);
+	if (help_folder_file.exists()) {
+	    return help_folder;
+	}
+
+	// Resort to English
+	return "help/" + StaticStrings.ENGLISH_LANGUAGE_STR  + "/";
+    }
+
+    /** Method to retrieve an image icon with the given filename found in classpath or the resouces directory.
+     *  @return The specified <strong>ImageIcon</strong>, or an error image replacement if no such images exists.
+     */
+    static public ImageIcon getImage(String filename) {
+	return getImage(filename, false);
+    }
+
+    static public ImageIcon getImage(String filename, boolean wait_until_complete) {
+	ImageIcon image = null;
+	try {
+	    image = new ImageIcon(base.getResource("/images/" + Dictionary.get("Version") + "/" + filename));
+	    //image = new ImageIcon(base.getResource("/images/" + filename));
+	}
+	catch(NullPointerException exception) {
+	    image = new ImageIcon(base.getResource("/images/" + filename));
+	}
+	if(image == null) {
+	    image = ERROR_ICON;
+	}
+
+	if(wait_until_complete) {
+	    int load_status;
+	    do {
+		load_status = image.getImageLoadStatus();
+	    }
+	    while(load_status != MediaTracker.ABORTED && load_status != MediaTracker.ERRORED && load_status != MediaTracker.COMPLETE);
+	}
+	return image;
+    }
+    //static public ImageIcon getImage(String filename) {
+	
+
+
+    /** Method which constructs the import directory given a certain collection.
+     * @param col_dir The location of the collection directory as a <strong>String</strong>.
+     * @return The location of the given collections import directory, also as a <strong>String</strong>.
+     */
+    public static String getImportDir(String col_dir) {
+	return col_dir + IMPORT_DIR;
+    }
+
+    /** Method which constructs the log directory given a certain collection.
+     * @param col_dir The location of the collection directory as a <strong>String</strong>.
+     * @return The location of the given collections log directory, also as a <strong>String</strong>.
+     */
+    public static String getLogDir(String col_dir) {
+	if(col_dir != null) {
+	    return col_dir + LOG_DIR;
+	}
+	else {
+	    return getGLIUserFolder().getAbsolutePath() + File.separator + LOG_DIR;
+	}
+    }
+    /** Determine this machines name.
+     * @return The name as a <strong>String</strong>.
+     */
+    static public String getMachineName() {
+	try {
+	    return InetAddress.getLocalHost().getHostName();
+	}
+	catch(UnknownHostException ex) {
+	}
+	return "Unknown Machine";
+    }
+
+
+    static public void extractFromJar(String filename, String dst_dir, boolean must_be_present) 
+    {
+	try {
+	    // setup input stream for slurping out file
+	    InputStream fis = base.getResourceAsStream("/"+filename);
+	    BufferedInputStream fbis = new BufferedInputStream(fis); 
+	    DataInputStream fdbis = new DataInputStream(fbis);
+		    
+	    // set up output stream for writing to disk
+	    String ofname = dst_dir + filename;
+	    FileOutputStream fos = new FileOutputStream(ofname);
+	    BufferedOutputStream bfos = new BufferedOutputStream(fos);
+
+	    byte[] buf = new byte[1024];
+	    int len;
+	    int total_bytes = 0;
+	    while ((len = fdbis.read(buf)) >= 0) {
+		bfos.write(buf,0,len);
+		total_bytes += len;
+	    }
+
+	    fdbis.close();
+	    bfos.close();
+	}
+	catch (Exception error) {
+	    if (must_be_present) {
+		error.printStackTrace();
+	    }
+	}
+    }
+
+
+    /** Method which unzips a given metadata resoure
+     * @param zip_fname The name of the zip file as a <strong>String</strong>.
+     * @param dst_dir The destination directory for unzipping, also as a <strong>String</strong>.
+     * @param use_classloader Used to determine whether or not to look in resource bundle.
+     */
+    static public void unzipFromJar(String jar_zip_fname, String dst_dir) {
+
+	File file = null;
+	if (!dst_dir.endsWith(File.separator)) {
+	    dst_dir += File.separator;
+	}
+
+	extractFromJar(jar_zip_fname,dst_dir,true);
+
+	String zip_ofname = dst_dir + jar_zip_fname;
+	File zip_file = new File(zip_ofname);
+
+	try {
+	    ZipFile zipfile = new ZipFile(zip_file);
+	    
+	    Enumeration e = zipfile.entries(); 
+	    while (e.hasMoreElements()) {
+		ZipEntry zipentry = (ZipEntry) e.nextElement();
+		String zentryname = dst_dir + zipentry.getName();
+		DebugStream.println("    Unzipping: " + zentryname);
+
+		if (zipentry.isDirectory()) {
+		    // Create named directory, if it doesn't already exist
+		    File zip_entry_file = new File(zentryname);
+		    if (!zip_entry_file.exists() && !zip_entry_file.mkdirs()) {
+			System.err.println("Error: unable to create directory " + zip_entry_file);
+		    }
+		}
+		else {
+		    // Write out file to disk
+
+		    // set up input stream
+		    InputStream zis = zipfile.getInputStream(zipentry); 
+		    BufferedInputStream bzis = new BufferedInputStream(zis); 
+		    DataInputStream dbzis = new DataInputStream(bzis);
+		    
+		    // set up output stream
+		    FileOutputStream fzos = new FileOutputStream(zentryname);
+		    BufferedOutputStream bfzos = new BufferedOutputStream(fzos);
+		    
+		    byte[] buf = new byte[1024];
+		    int len;
+		    while ((len = dbzis.read(buf)) >= 0) {
+			bfzos.write(buf,0,len);
+		    }
+		    
+		    dbzis.close();
+		    bfzos.close();
+		}
+	    }
+	} 
+	catch (ZipException error) {
+	    System.err.println("Error: Unable to open '"+zip_file.getAbsolutePath()+"'");
+	    DebugStream.printStackTrace(error);
+	}
+
+	catch (Exception error) {
+	    error.printStackTrace();
+	}
+    }
+    
+
+    static protected String unixStylePath(String path)
+    {
+	String unix_path = path.replace('\\','/');
+	return unix_path;
+    }
+
+    /*
+      Method to handle zipping up of a file. Called by zipup and dirFunc.
+
+      @param zos the zip output stream, as a <strong>ZipOutputStream</strong>
+      @param file_path the <strong>String</strong> containing the path to the file/directory tozip up
+      @param prefix_strip the <strong>int</strong> used to substring the file_path
+      @param source the <strong>GShell</strong> that is calling this method, so that we can check to see if the process has been cancelled. null if GShell is not the calling object.
+      @param accept_expr a <strong>String</strong> containing a regular expression of files to include in the archive. All other files will be excluded.
+      @param reject_expr a <strong>String</strong> containing a regular expression of files not to include in the archive.
+
+      @return boolean
+
+      @see dirFunc
+      @see zipup
+     */
+    /*    static protected boolean zipFunc (ZipOutputStream zos, String file_path, int prefix_strip, GShell source, boolean encountered_file, String accept_expr, String reject_expr) 
+    {
+	String new_file_path = file_path;
+
+	if((reject_expr != "") && (file_path.matches(reject_expr))) {
+	    // matches reject expression
+	    //DebugStream.println("File \'" + file_path + "\' matches the reject expression \'" + reject_expr + "\'");
+	    return encountered_file;
+	}
+
+	if ((accept_expr != "") && (!file_path.matches(accept_expr))) {
+	    // does not match accept expression
+	    //DebugStream.println("File \'" + file_path + "\' doesn't match accept expression \'" + accept_expr + "\'");
+	    return encountered_file;
+	}
+
+	//Special case: we only want the metadata. Parse the xml files.
+	if(accept_expr.compareTo(".*doc.xml") == 0) {
+	    DebugStream.println("Only want to get metadata");
+	    Document old_document = XMLTools.parseXMLFile(new File(file_path));
+	    NodeList content = old_document.getElementsByTagName("Content");
+
+	    try {
+		for(int i=content.getLength()-1; i >= 0; i--) {
+		    //Remove all content - just leave the metadata.
+		    content.item(i).getParentNode().removeChild(content.item(i));
+		}
+	    }
+	    catch (Exception ex) {
+		ex.printStackTrace();
+	    }
+
+	    new_file_path = file_path.substring(0, file_path.lastIndexOf(File.separator) + 1) + "new.xml";
+	    //Is there any better way than writing out as a new file, then reading back in??
+	    XMLTools.writeXMLFile(new File(new_file_path), old_document);
+	}
+
+	// Using try is required because of file io.
+	try {
+	    // Exit if pressed cancel.
+	    if (source != null && source.hasSignalledStop()) {
+		return false;
+	    }
+
+	    // Create a Zip Entry and put it into the archive (no data yet).
+
+	    // Strip off col_dir prefix
+	    String zip_path = file_path.substring(prefix_strip);
+	    // Zip files use '/' for directory separator
+	    String unix_style_path = unixStylePath(zip_path);
+
+	    ZipEntry fileEntry = new ZipEntry(unix_style_path);
+	    zos.putNextEntry(fileEntry);
+
+	    // Create a file input stream and a buffered input stream.
+	    FileInputStream fis = new FileInputStream(new_file_path);
+	    BufferedInputStream bis = new BufferedInputStream(fis);
+	    
+	    // Create a byte array object named data and declare byte count variable.
+	    byte[] data = new byte[1024];
+	    int byteCount;
+	    // Create a loop that reads from the buffered input stream and writes
+	    // to the zip output stream until the bis has been entirely read.
+	    while ((byteCount = bis.read(data, 0, 1024)) > -1) {
+		//Check for cancell
+		if(source != null) {
+		    if(source.hasSignalledStop()) {
+			break;
+		    }
+		}
+		zos.write(data, 0, byteCount);
+	    }
+	    encountered_file = true;
+	} 
+	catch (IOException e) {
+	    e.printStackTrace();
+	}
+	
+	DebugStream.println("Zipping up: " + file_path);
+	return encountered_file;
+	}*/
+
+    /*
+      Method to handle ziping up of a directory.
+
+      @param zos
+      @param dir_name
+      @param prefix_strip
+      @param source
+      @param encountered_file ??What's this??
+      @param accept_expr
+      @param reject_expr
+
+      @see zipup
+      @see zipFunc
+     */
+    /*    static protected boolean dirFunc (ZipOutputStream zos, String dir_name, int prefix_strip, GShell source, boolean encountered_file, String accept_expr, String reject_expr) 
+    {
+	File dirObj = new File(dir_name);
+
+	if (dirObj.exists() == true) {
+	    if (dirObj.isDirectory() == true) {
+		// Create an array of File objects, one for each file or directory in dirObj.
+		File [] fileList = dirObj.listFiles();
+		
+		// Loop through File array and display.
+		for (int i = 0; i < fileList.length; i++) {
+		    if(source != null && source.hasSignalledStop()) { break; }
+		    File file = fileList[i];
+		    if (file.isDirectory()) {
+			String dir_path = file.getPath();
+			String zip_path = dir_path.substring(prefix_strip);
+			// Zip files use '/' for directory separator
+			String unix_style_path 
+			    = unixStylePath(zip_path+File.separator);
+			ZipEntry dirEntry = new ZipEntry(unix_style_path);
+
+			try {
+			    zos.putNextEntry(dirEntry);
+			} 
+			catch (IOException e) {
+			    e.printStackTrace();
+			}
+
+			encountered_file = dirFunc(zos, dir_path, prefix_strip, source, encountered_file, accept_expr, reject_expr);
+		    } else if (file.isFile()) {
+			// Call the zipFunc function
+			String file_path = fileList[i].getPath();
+			encountered_file = zipFunc(zos,file_path,prefix_strip, source, encountered_file, accept_expr, reject_expr);
+		    }
+		}
+	    } 
+	    else { 
+		System.err.println (dir_name+" is not a directory."); 
+	    }
+	} 
+	else { 
+	    System.err.println ("Directory "+dir_name+" does not exist."); 
+	}
+	return encountered_file;
+	}*/
+    
+    /**
+      Method to zip up a given file or directory
+
+      @param col_dir
+      @param col_name
+      @param dir_or_file
+      @param source
+      @param filter
+
+      @see zipFunc
+      @see dirFunc
+     */
+    /*static public boolean zipup(String col_dir, String col_name, String dir_or_file, GShell source, String accept_expr, String reject_expr)
+    {
+	int prefix_strip = col_dir.length();
+	boolean encountered_file = false;
+
+	String zip_fname = col_dir + col_name + ".zip";
+	String zip_dir_or_file = col_dir + col_name + File.separator + dir_or_file;
+	File zip_dof = new File(zip_dir_or_file);
+
+	try {
+	    FileOutputStream fos = new FileOutputStream(zip_fname);
+	    ZipOutputStream zos = new ZipOutputStream(fos);
+	    
+	    if (zip_dof.exists()) {
+		if (zip_dof.isDirectory()) {
+		    String zip_dir = zip_dir_or_file;
+		    encountered_file = dirFunc(zos, zip_dir, prefix_strip, source, encountered_file, accept_expr, reject_expr);
+		}
+		else {
+		    String zip_full_file = zip_dir_or_file; 
+		    String zip_path = zip_full_file.substring(prefix_strip);
+
+		    for (int i=1; i<zip_path.length(); i++) {
+			String ch = String.valueOf(zip_path.charAt(i));
+
+			if (ch.equals(File.separator)) {
+			    String dir_path = zip_path.substring(0,i);
+			    // Zip files use '/' for directory separator
+			    String unix_style_path 
+				= unixStylePath(dir_path+File.separator);
+			    ZipEntry dirEntry = new ZipEntry(unix_style_path);
+			    zos.putNextEntry(dirEntry);
+			}
+		    }
+		    encountered_file = zipFunc(zos, zip_full_file, prefix_strip, source, encountered_file, accept_expr, reject_expr);
+		}
+	    }
+	    else {
+		System.err.println("Warning: " + zip_dir_or_file + " does not exist!");
+	    }
+	    
+	    // Close the file output streams for both the file and the zip.
+	    zos.flush();
+	    zos.close();
+	    fos.close();
+	} 
+	catch (IOException exception) {
+	    DebugStream.printStackTrace(exception);
+	    return false;
+	}
+	return encountered_file;
+	} */
+    
+
+    static public void unzip(String col_dir, String col_name)
+    {
+	String zip_fname = col_dir + col_name + ".zip";
+	int zip_mode = ZipFile.OPEN_READ | ZipFile.OPEN_DELETE;
+
+	try {
+	    ZipFile zipfile = new ZipFile(new File(zip_fname), zip_mode);
+	    
+	    Enumeration e = zipfile.entries(); 
+	    while (e.hasMoreElements()) {
+		ZipEntry zipentry = (ZipEntry) e.nextElement();
+		String zentryname = col_dir + zipentry.getName();
+		File file = new File(zentryname);
+		DebugStream.println("    Unzipping: " + zentryname);
+
+		if (zipentry.isDirectory()) {
+		    // Create named directory, if it doesn't already exist
+		    if (!file.exists() && !file.mkdirs()) {
+			System.err.println("Error: unable to create directory " + file);
+		    }
+		}
+		else {
+		    // Write out file to disk
+
+		    //Make sure it's parent directory exists.
+		    File dir = new File(file.getParent());
+		    dir.mkdirs();
+
+		    // set up input stream
+		    InputStream zis = zipfile.getInputStream(zipentry); 
+		    BufferedInputStream bzis = new BufferedInputStream(zis); 
+		    DataInputStream dbzis = new DataInputStream(bzis);
+		    
+		    // set up output stream
+		    FileOutputStream fzos = new FileOutputStream(zentryname);
+		    BufferedOutputStream bfzos = new BufferedOutputStream(fzos);
+		    
+		    byte[] buf = new byte[1024];
+		    int len;
+		    while ((len = dbzis.read(buf)) >= 0) {
+			bfzos.write(buf,0,len);
+		    }
+		    
+		    dbzis.close();
+		    bfzos.close();
+		}
+	    }
+	} 
+	catch (ZipException error) {
+	    System.err.println("Error: Unable to open '"+zip_fname+"'");
+	    System.err.println("This maybe caused by the zip file being empty.");
+	    DebugStream.printStackTrace(error);
+	}
+
+	catch (Exception error) {
+	    error.printStackTrace();
+	}
+    }
+
+
+    static public String getSitesDir(String gsdl3_path) {
+	return gsdl3_path + File.separator + "web"
+	    + File.separator + "sites" + File.separator;
+
+    }
+
+
+    /** returns the path to the greenstone version of wget */
+    static public String getWGetPath(String gsdl_path)
+    {
+	if (isWindows()) {
+	    return gsdl_path + "bin" + File.separator + "windows" + File.separator + "wget.exe";
+	}
+	else if (isMac()) {
+	    return gsdl_path + "bin" + File.separator + "darwin" + File.separator + "wget";
+	}
+	else {
+	    return gsdl_path + "bin" + File.separator + "linux" + File.separator + "wget";
+	}
+    }
+
+
+    static public void initImages(Object base_object)
+    {
+	base = base_object.getClass();
+	//System.err.println("Base: " + base);
+
+	BLANK_ICON = new ImageIcon(base.getResource("/images/blank.gif"));
+	ERROR_ICON = new ImageIcon(base.getResource("/images/error.gif"));
+	HELP_ICON  = new ImageIcon(base.getResource("/images/help.gif"));
+    }
+
+
+    /** Method to determine if the host system is MacOS based.
+     * @return a boolean which is true if the platform is MacOS, false otherwise
+     */
+    public static boolean isMac() {
+	Properties props = System.getProperties();
+	String os_name = props.getProperty("os.name","");
+	if(os_name.startsWith("Mac OS")) {
+	    return true;
+	}
+	return false;
+    }
+
+   
+    /** Method to determine if the host system is Microsoft Windows based.
+     * @return A <i>boolean</i> which is <i>true</i> if the platform is Windows, <i>false</i> otherwise.
+     */
+    public static boolean isWindows() {
+	Properties props = System.getProperties();
+	String os_name = props.getProperty("os.name","");
+	if(os_name.startsWith("Windows")) {
+	    return true;
+	}
+	return false;
+    }
+
+    public static boolean isWindows9x() {
+	Properties props = System.getProperties();
+	String os_name = props.getProperty("os.name","");
+	if(os_name.startsWith("Windows") && os_name.indexOf("9") != -1) {
+	    return true;
+	}
+	return false;
+    }
+    /** Takes a string and a desired length and pads out the string to the length by adding spaces to the left.
+     * @param str The target <strong>String</strong> that needs to be padded.
+     * @param length The desired length of the string as an <i>int</i>.
+     * @return A <strong>String</strong> made from appending space characters with the string until it has a length equal to length.
+     */
+    static private String pad(String str_raw, int length, char fill, boolean end) {
+	StringBuffer str = new StringBuffer(str_raw);
+	while(str.length() < length) {
+	    if(end) {
+		str.insert(0, fill);
+	    }
+	    else {
+		str.append(fill);
+	    }
+	}
+	return str.toString();
+    }
+
+
+    /** Parse in a xml document from a given filename. Note that this filename may need to be resolved by the class loader, especially for template files within a jar. */
+    static public Document parse(String filename, boolean use_classloader)
+    {
+	// Try the class loader if desired (for the applet JAR file)
+	if (use_classloader) {
+	    InputStream is = base.getResourceAsStream("/" + filename);
+	    if (is != null) {
+		return XMLTools.parseXML(is);
+	    }
+	}
+
+	// Try the file outside the classes directory
+	return XMLTools.parseXMLFile(new File(filename));
+    }
+
+
+    static public StringBuffer readXMLStream(InputStream input_stream)
+    {
+	StringBuffer xml = new StringBuffer("");
+
+	try {
+	    InputStreamReader isr = new InputStreamReader(input_stream, "UTF-8");
+	    BufferedReader buffered_in = new BufferedReader(isr);
+	    
+	    String line = "";
+	    boolean xml_content = false;
+	    while((line = buffered_in.readLine()) != null) {
+		if(xml_content) {
+		    xml.append(line);
+		    xml.append("\n");
+		}
+		else if(line.trim().startsWith("<?xml")) {
+		    xml_content = true;
+		    xml.append(line);
+		    xml.append("\n");
+		}
+	    }
+	    buffered_in = null;
+	}
+	catch (Exception error) {
+	    System.err.println("Failed when trying to parse XML stream");
+	    error.printStackTrace();
+	}
+
+	return xml;
+    }
+
+
+    /** I think this works a bit better on Unicode strings. */
+    static public String stripNL(String raw_string)
+    {
+	String stripped_string = new String();
+	for (int i = 0; i < raw_string.length(); i++) {
+	    char raw_character = raw_string.charAt(i);
+	    if (raw_character != '\n' && raw_character != '\t') {
+		stripped_string = stripped_string + raw_character;
+	    }
+	}
+	return stripped_string;
+    }
+}
Index: branches/ant-install-branch/gsdl3/src/java/org/greenstone/core/util/XMLTools.java
===================================================================
--- branches/ant-install-branch/gsdl3/src/java/org/greenstone/core/util/XMLTools.java	(revision 9974)
+++ branches/ant-install-branch/gsdl3/src/java/org/greenstone/core/util/XMLTools.java	(revision 9974)
@@ -0,0 +1,224 @@
+package org.greenstone.core.util;
+
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import org.apache.xerces.parsers.*;
+import org.apache.xml.serialize.*;
+import org.greenstone.core.DebugStream;
+import org.w3c.dom.*;
+import org.xml.sax.*;
+
+
+/** This class is a static class containing useful XML functions */
+public class XMLTools
+{
+    /** Remove all of the child nodes from a certain node. */
+    static final public void clear(Node node)
+    {
+	while (node.hasChildNodes()) {
+	    node.removeChild(node.getFirstChild());
+	}
+    }
+
+
+    static public ArrayList getChildElementsByTagName(Element parent_element, String element_name)
+    {
+	ArrayList child_elements = new ArrayList();
+
+	NodeList children_nodelist = parent_element.getChildNodes();
+	for (int i = 0; i < children_nodelist.getLength(); i++) {
+	    Node child_node = children_nodelist.item(i);
+	    if (child_node.getNodeType() == Node.ELEMENT_NODE && child_node.getNodeName().equals(element_name)) {
+		child_elements.add(child_node);
+	    }
+	}
+
+	return child_elements;
+    }
+
+
+    static public String getElementTextValue(Element element)
+    {
+	// Find the first text node child
+	NodeList children_nodelist = element.getChildNodes();
+	for (int i = 0; i < children_nodelist.getLength(); i++) {
+	    Node child_node = children_nodelist.item(i);
+	    if (child_node.getNodeType() == Node.TEXT_NODE) {
+		return child_node.getNodeValue();
+	    }
+	}
+
+	// None found
+	return "";
+    }
+
+
+    /** Method to retrieve the value of a given node.
+     * @param element The <strong>Element</strong> whose value we wish to find.
+     * Soon to be deprecated!
+     */
+    static final public String getValue(Node element) {
+	// If we've been given a subject node first retrieve its value node.
+	if(element.getNodeName().equals("Subject")) {
+	    element = getNodeFromNamed(element, "Value");
+	}
+	// If we've got a value node, then reconstruct the text. Remember that DOM will split text over 256 characters into several text nodes
+	if(element != null && element.hasChildNodes()) {
+	    StringBuffer text_buffer = new StringBuffer();
+	    NodeList text_nodes = element.getChildNodes();
+	    for(int i = 0; i < text_nodes.getLength(); i++) {
+		Node possible_text = text_nodes.item(i);
+		if(possible_text.getNodeName().equals(StaticStrings.TEXT_NODE)) {
+		    text_buffer.append(possible_text.getNodeValue());
+		}
+	    }
+	    return text_buffer.toString();
+	}
+	return "";
+    }
+
+
+    /** Method to retrieve from the node given, a certain child node with the specified name.
+     * @param parent The <strong>Node</strong> whose children should be searched.
+     * @param name The required nodes name as a <strong>String</strong>.
+     * @return The requested <strong>Node</strong> if it is found, <i>null</i> otherwise.
+     * Soon to be deprecated!
+     */ 
+    static final public Node getNodeFromNamed(Node parent, String name) {
+	Node child = null;
+	for(Node i = parent.getFirstChild(); i != null && child == null; 
+	    i = i.getNextSibling()) {
+	    if(i.getNodeName().equals(name)) {
+		child = i;
+	    }
+	}
+	return child;
+    }
+
+
+    /** Parse an XML document from a given file */
+    static public Document parseXMLFile(File xml_file)
+    {
+	// No file? No point trying!
+	if (xml_file.exists() == false) {
+	    return null;
+	}
+
+	try {
+	    return parseXML(new FileInputStream(xml_file));
+	}
+	catch (Exception exception) {
+	    DebugStream.printStackTrace(exception);
+	    return null;
+	}
+    }
+
+
+    /** Parse an XML document from a given input stream */
+    static public Document parseXML(InputStream xml_input_stream)
+    {
+	Document document = null;
+
+	try {
+	    InputStreamReader isr = new InputStreamReader(xml_input_stream, "UTF-8");
+	    Reader xml_reader = new BufferedReader(isr);
+	    document = parseXML(xml_reader);
+	    isr.close();
+	    xml_input_stream.close();
+	}
+	catch (Exception exception) {
+	    DebugStream.printStackTrace(exception);
+	}
+
+	return document;
+    }
+
+
+    /** Parse an XML document from a given reader */
+    static public Document parseXML(Reader xml_reader)
+    {
+	Document document = null;
+
+	try {
+	    InputSource isc       = new InputSource(xml_reader);
+	    DOMParser parser      = new DOMParser();
+	    parser.setFeature("http://xml.org/sax/features/validation", false);
+	    parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+	    // May or may not be ignored, the documentation for Xerces is contradictory. If it works then parsing -should- be faster.
+	    parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);
+	    parser.setFeature("http://apache.org/xml/features/dom/include-ignorable-whitespace", false);
+	    parser.parse(isc);
+	    document = parser.getDocument();
+	}
+	catch (Exception exception) {
+	    DebugStream.printStackTrace(exception);
+	}
+
+	return document;
+    }
+
+
+    /** Removes characters that are invalid in XML (see http://www.w3.org/TR/2000/REC-xml-20001006#charsets) */
+    static public String removeInvalidCharacters(String text)
+    {
+	char[] safe_characters = new char[text.length()];
+	int j = 0;
+
+	char[] raw_characters = new char[text.length()];
+	text.getChars(0, text.length(), raw_characters, 0);
+	for (int i = 0; i < raw_characters.length; i++) {
+	    char character = raw_characters[i];
+	    if ((character >= 0x20 && character <= 0xD7FF) || character == 0x09 || character == 0x0A || character == 0x0D || (character >= 0xE000 && character <= 0xFFFD) || (character >= 0x10000 && character <= 0x10FFFF)) {
+		safe_characters[j] = character;
+		j++;
+	    }
+	}
+
+	return new String(safe_characters, 0, j);
+    }
+
+
+    /** Set the #text node value of some element.
+     * @param element the Element whose value we wish to set
+     * @param value the new value for the element as a String
+     * Soon to be deprecated!
+     */
+    static final public void setValue(Element element, String value) {
+	// Remove any existing child node(s)
+	clear(element);
+	// Add new text node.
+	if (value != null) {
+	    element.appendChild(element.getOwnerDocument().createTextNode(value));
+	}
+    }
+
+
+    /** Write an XML document to a given file */
+    static public void writeXMLFile(File xml_file, Document document)
+    {
+	try {
+	    OutputStream os = new FileOutputStream(xml_file);
+	    // Create an output format for our document.
+	    OutputFormat f = new OutputFormat(document);
+	    f.setEncoding("UTF-8");
+	    f.setIndenting(true);
+	    f.setLineWidth(0); // Why isn't this working!
+	    f.setPreserveSpace(false);
+	    // Create the necessary writer stream for serialization.
+	    OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
+	    Writer w               = new BufferedWriter(osw);
+	    // Generate a new serializer from the above.
+	    XMLSerializer s        = new XMLSerializer(w, f);
+	    s.asDOMSerializer();
+	    // Finally serialize the document to file.
+	    s.serialize(document);
+	    // And close.
+	    os.close();
+	}
+	catch (Exception exception) {
+	    DebugStream.printStackTrace(exception);
+	}
+    }
+}
