Ignore:
Timestamp:
2003-05-27T15:40:47+12:00 (21 years ago)
Author:
mdewsnip
Message:

Fixed tabbing.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/gli/src/org/greenstone/gatherer/util/Utility.java

    r4344 r4364  
    5353 */
    5454public class Utility {
    55     /** The default size of a gatherer progress bar, in either the download view or the build view. */
    56     static final public Dimension PROGRESS_BAR_SIZE = new Dimension(580,65);
    57     /** The number of kilobytes to use as a io buffer. */
    58     static final public int FACTOR = 1;
    59     /** The size of the io buffer, calculated as FACTOR * 1024. */
    60     static final public int BUFFER_SIZE = FACTOR * 1024;
    61     /** Definition of an important directory name, in this case the archive directory for the collection. */
    62     static final public String ARCHIVE_DIR = "archives" + File.separator;
    63     /** Definition of an important directory name, in this case the base dir, or the working directory of the Gatherer. */
    64     static final public String BASE_DIR  = System.getProperty("user.dir") + File.separator;
    65     /** Definition of an important directory name, in this case the building directory for the collection. */
    66     static final public String BUILD_DIR = "building" + File.separator;
    67     /** Definition of an important directory name, in this case the public web cache for the Gatherer. */
    68     static final public String CACHE_DIR = BASE_DIR + "cache" + File.separator;
    69     static final public String CFG_COLLECTIONMETA_COLLECTIONNAME = "collectionmeta collectionname";
    70     static final public String CFG_COLLECTIONMETA_COLLECTIONEXTRA = "collectionmeta collectionextra";
    71     static final public String CFG_COLLECTIONMETA_ICONCOLLECTION = "collectionmeta iconcollection";
    72     static final public String CFG_CLASSIFY = "classify";
    73     static final public String CFG_CLASSIFY_BUTTONNAME = "-buttonname";
    74     static final public String CFG_CLASSIFY_HFILE = "-hfile";
    75     static final public String CFG_CLASSIFY_METADATA = "-metadata";
    76     static final public String CFG_CLASSIFY_SORT = "-sort";
    77     static final public String CFG_CREATOR = "creator";
    78     static final public String CFG_FORMAT = "format";
    79     static final public String CFG_MAINTAINER = "maintainer";
    80     /** Definition of an important directory name, in this case the parent directory of all the collections in the gsdl. */
    81     static final public String COL_DIR = "collect" + File.separator;
    82     static final public String COLLECTION_DEMO = "greenstone demo";
    83     static final public String COLLECTION_DEMO_DIRECTORY = "demo" + File.separator;
    84     static final public String COLLECTION_DLS = "Development Library Subset";
    85     static final public String COLLECTION_DLS_DIRECTORY = "dls" + File.separator;
    86     static final public String COLLECTION_TREE = "Collection";
    87     /** Definition of an important directory name, in this case the file the collection configuration is expect to be in. */
    88     static final public String CONFIG_DIR = "etc" + File.separator + "collect.cfg";
    89     /** The default file name for the urls missing any file. */
    90     static final public String DEFAULT_FILE = "index.html";
    91     static final public String DEFAULT_NAMESPACE = "gsp";
    92     /** The default protocol header for those urls missing any protocol. */
    93     static final public String DEFAULT_PROTOCOL = "http://";
    94     /** The default dictionary to load. */
    95     static final public String DICTIONARY = "dictionary";
    96     static final public String DLS_MDS = "dls.mds";
    97     static final public String ENGLISH_VALUE = "en";
    98     /** Definition of an important directory name, in this case the etc (or extra information) directory for the collection. */
    99     static final public String ETC_DIR = "etc" + File.separator;
    100     static final public String EXTRACTED_METADATA_NAMESPACE = "ex";
    101     /** The location of the default greenstone metadata file. */
    102     static final public String GREENSTONEDIRECTORYMETADATA_TEMPLATE = "xml/metadata.xml";
    103     /** Definition of an important directory name, in this case the private web cache directory for the collection. */
    104     static final public String GCACHE_DIR = "gcache" + File.separator;
    105 /** Definition of an important directory name, in this case the location of help documentation. */
    106     static final public String HELP_DIR = BASE_DIR + "help" + File.separator;
    107     /** Definition of an important directory name, in this case the import directory for the collection. */
    108     static final public String IMPORT_DIR = "gimport" + File.separator;
    109     /** Definition of an important directory name, in this case the index directory for the collection. */
    110     static final public String INDEX_DIR = "index" + File.separator;
    111     static final public String LANGUAGE_ATTRIBUTE = "language";
    112     /** Definition of an important directory name, in this case the log directory for the collection. */
    113     static final public String LOG_DIR = "log" + File.separator;
    114     /** Definition of an important directory name, in this case the location of the expected collection metadata sets.. */
    115     static final public String META_DIR = "metadata" + File.separator; // Col. Copy
    116     /** Definition of an important directory name, in this case the location of the default metadata sets. */
    117     static final public String METADATA_DIR = BASE_DIR + "metadata" + File.separator;
    118     static final public String METADATA_EXTRACTED = "extracted.mds";
    119     /** The location the gatherer expects to find metadata set information. */
    120     static final public String METADATA_SET_TEMPLATE = "xml" + File.separator + "template.mds";
    121     static final public String METADATA_VALUE_TEMPLATE = "xml" + File.separator + "template.mdv";
    122     static final public String METADATA_XML = "metadata.xml";
    123     static final public String NAME_ELEMENT = "Name";
    124     /** Definition of an important directory name, in this case the import directory for the collection. */
    125     static final public String OLD_IMPORT_DIR = "import" + File.separator;
    126     /** The default name of the perl executable under unix. */
    127     static final public String PERL_EXECUTABLE_UNIX = "perl";
    128     /** The default name of the perl executable under windows. */
    129     static final public String PERL_EXECUTABLE_WINDOWS = "Perl.exe";
    130     /** The name of the Gatherer. */
    131     static final public String PROGRAM_NAME = "The Librarian Interface";
    132     /** The current version of the Gatherer. */
    133     static final public String PROGRAM_VERSION = "ver 2.0";
    134     /** Definition of an important directory name, in this case the location of the recycled files location. */
    135     static final public String RECYCLE = BASE_DIR + "recycle" + File.separator;
    136     /** Definition of an important directory name, in this case the location of image and other resources. */
    137     static final public String RES_DIR = BASE_DIR + "resource" + File.separator;
    138     static final public String SERVER_EXE = "server.exe";
    139     /** Definition of an important directory name, in this case the location of opening (or welcome) screen html. */
    140     static final public String WELCOME_DIR = BASE_DIR + "welcome" + File.separator;
    141     static final public String WORKSPACE_TREE = "Workspace";
    142     // These are out of alphabetic order to avoid forward reference error.
    143     /** The default icon to produce a 'help-icon' sized blank space before a menu entry. */
    144     static final public ImageIcon BLANK_ICON = new ImageIcon(ClassLoader.getSystemResource("images/blank.gif"));
    145     /** The default error icon image. */
    146     static final public ImageIcon ERROR_ICON = new ImageIcon(ClassLoader.getSystemResource("images/error.gif"));
    147     static final public ImageIcon HELP_ICON = new ImageIcon(ClassLoader.getSystemResource("images/help.gif"));
    148     /** The image for a toggle button whose state is 'on'. */
    149     static final public ImageIcon ON_ICON = new ImageIcon(ClassLoader.getSystemResource("images/check.gif"));
    150     /** The image for a toggle button whose state is 'off'. */
    151     static final public ImageIcon OFF_ICON = new ImageIcon(ClassLoader.getSystemResource("images/cross.gif"));
    152     /** Method to turn a file from with the system file tree into a tree path for insertion into a tree.
    153       * @param file The <strong>File</strong> whose tree path you are attempting to discover.
    154       * @param in_col A <i>boolean</i> indicating whether we are looking for a file within a collection of not. If <i>true</i> then the tree paths head in the collection name, and no element in the path refers to the import directory. Otherwise the paths head will be one of the system roots and all traversed file locations will exist in the path.
    155       * @return A <strong>TreePath</strong> which traverses the file system tree to the specified file.
    156       */
    157     public static TreePath createTreePath(File file, boolean in_col) {
    158           TreePath path = null;
    159           // Get the absolute path of the file.
    160           String abs_path = file.getAbsolutePath();
    161           while(file != null) {
     55    /** The default size of a gatherer progress bar, in either the download view or the build view. */
     56    static final public Dimension PROGRESS_BAR_SIZE = new Dimension(580,65);
     57    /** The number of kilobytes to use as a io buffer. */
     58    static final public int FACTOR = 1;
     59    /** The size of the io buffer, calculated as FACTOR * 1024. */
     60    static final public int BUFFER_SIZE = FACTOR * 1024;
     61    /** Definition of an important directory name, in this case the archive directory for the collection. */
     62    static final public String ARCHIVE_DIR = "archives" + File.separator;
     63    /** Definition of an important directory name, in this case the base dir, or the working directory of the Gatherer. */
     64    static final public String BASE_DIR  = System.getProperty("user.dir") + File.separator;
     65    /** Definition of an important directory name, in this case the building directory for the collection. */
     66    static final public String BUILD_DIR = "building" + File.separator;
     67    /** Definition of an important directory name, in this case the public web cache for the Gatherer. */
     68    static final public String CACHE_DIR = BASE_DIR + "cache" + File.separator;
     69    static final public String CFG_COLLECTIONMETA_COLLECTIONNAME = "collectionmeta collectionname";
     70    static final public String CFG_COLLECTIONMETA_COLLECTIONEXTRA = "collectionmeta collectionextra";
     71    static final public String CFG_COLLECTIONMETA_ICONCOLLECTION = "collectionmeta iconcollection";
     72    static final public String CFG_CLASSIFY = "classify";
     73    static final public String CFG_CLASSIFY_BUTTONNAME = "-buttonname";
     74    static final public String CFG_CLASSIFY_HFILE = "-hfile";
     75    static final public String CFG_CLASSIFY_METADATA = "-metadata";
     76    static final public String CFG_CLASSIFY_SORT = "-sort";
     77    static final public String CFG_CREATOR = "creator";
     78    static final public String CFG_FORMAT = "format";
     79    static final public String CFG_MAINTAINER = "maintainer";
     80    /** Definition of an important directory name, in this case the parent directory of all the collections in the gsdl. */
     81    static final public String COL_DIR = "collect" + File.separator;
     82    static final public String COLLECTION_DEMO = "greenstone demo";
     83    static final public String COLLECTION_DEMO_DIRECTORY = "demo" + File.separator;
     84    static final public String COLLECTION_DLS = "Development Library Subset";
     85    static final public String COLLECTION_DLS_DIRECTORY = "dls" + File.separator;
     86    static final public String COLLECTION_TREE = "Collection";
     87    /** Definition of an important directory name, in this case the file the collection configuration is expect to be in. */
     88    static final public String CONFIG_DIR = "etc" + File.separator + "collect.cfg";
     89    /** The default file name for the urls missing any file. */
     90    static final public String DEFAULT_FILE = "index.html";
     91    static final public String DEFAULT_NAMESPACE = "gsp";
     92    /** The default protocol header for those urls missing any protocol. */
     93    static final public String DEFAULT_PROTOCOL = "http://";
     94    /** The default dictionary to load. */
     95    static final public String DICTIONARY = "dictionary";
     96    static final public String DLS_MDS = "dls.mds";
     97    static final public String ENGLISH_VALUE = "en";
     98    /** Definition of an important directory name, in this case the etc (or extra information) directory for the collection. */
     99    static final public String ETC_DIR = "etc" + File.separator;
     100    static final public String EXTRACTED_METADATA_NAMESPACE = "ex";
     101    /** The location of the default greenstone metadata file. */
     102    static final public String GREENSTONEDIRECTORYMETADATA_TEMPLATE = "xml/metadata.xml";
     103    /** Definition of an important directory name, in this case the private web cache directory for the collection. */
     104    static final public String GCACHE_DIR = "gcache" + File.separator;
     105    /** Definition of an important directory name, in this case the location of help documentation. */
     106    static final public String HELP_DIR = BASE_DIR + "help" + File.separator;
     107    /** Definition of an important directory name, in this case the import directory for the collection. */
     108    static final public String IMPORT_DIR = "gimport" + File.separator;
     109    /** Definition of an important directory name, in this case the index directory for the collection. */
     110    static final public String INDEX_DIR = "index" + File.separator;
     111    static final public String LANGUAGE_ATTRIBUTE = "language";
     112    /** Definition of an important directory name, in this case the log directory for the collection. */
     113    static final public String LOG_DIR = "log" + File.separator;
     114    /** Definition of an important directory name, in this case the location of the expected collection metadata sets.. */
     115    static final public String META_DIR = "metadata" + File.separator; // Col. Copy
     116    /** Definition of an important directory name, in this case the location of the default metadata sets. */
     117    static final public String METADATA_DIR = BASE_DIR + "metadata" + File.separator;
     118    static final public String METADATA_EXTRACTED = "extracted.mds";
     119    /** The location the gatherer expects to find metadata set information. */
     120    static final public String METADATA_SET_TEMPLATE = "xml" + File.separator + "template.mds";
     121    static final public String METADATA_VALUE_TEMPLATE = "xml" + File.separator + "template.mdv";
     122    static final public String METADATA_XML = "metadata.xml";
     123    static final public String NAME_ELEMENT = "Name";
     124    /** Definition of an important directory name, in this case the import directory for the collection. */
     125    static final public String OLD_IMPORT_DIR = "import" + File.separator;
     126    /** The default name of the perl executable under unix. */
     127    static final public String PERL_EXECUTABLE_UNIX = "perl";
     128    /** The default name of the perl executable under windows. */
     129    static final public String PERL_EXECUTABLE_WINDOWS = "Perl.exe";
     130    /** The name of the Gatherer. */
     131    static final public String PROGRAM_NAME = "The Librarian Interface";
     132    /** The current version of the Gatherer. */
     133    static final public String PROGRAM_VERSION = "ver 2.0";
     134    /** Definition of an important directory name, in this case the location of the recycled files location. */
     135    static final public String RECYCLE = BASE_DIR + "recycle" + File.separator;
     136    /** Definition of an important directory name, in this case the location of image and other resources. */
     137    static final public String RES_DIR = BASE_DIR + "resource" + File.separator;
     138    static final public String SERVER_EXE = "server.exe";
     139    /** Definition of an important directory name, in this case the location of opening (or welcome) screen html. */
     140    static final public String WELCOME_DIR = BASE_DIR + "welcome" + File.separator;
     141    static final public String WORKSPACE_TREE = "Workspace";
     142    // These are out of alphabetic order to avoid forward reference error.
     143    /** The default icon to produce a 'help-icon' sized blank space before a menu entry. */
     144    static final public ImageIcon BLANK_ICON = new ImageIcon(ClassLoader.getSystemResource("images/blank.gif"));
     145    /** The default error icon image. */
     146    static final public ImageIcon ERROR_ICON = new ImageIcon(ClassLoader.getSystemResource("images/error.gif"));
     147    static final public ImageIcon HELP_ICON = new ImageIcon(ClassLoader.getSystemResource("images/help.gif"));
     148    /** The image for a toggle button whose state is 'on'. */
     149    static final public ImageIcon ON_ICON = new ImageIcon(ClassLoader.getSystemResource("images/check.gif"));
     150    /** The image for a toggle button whose state is 'off'. */
     151    static final public ImageIcon OFF_ICON = new ImageIcon(ClassLoader.getSystemResource("images/cross.gif"));
     152    /** Method to turn a file from with the system file tree into a tree path for insertion into a tree.
     153     * @param file The <strong>File</strong> whose tree path you are attempting to discover.
     154     * @param in_col A <i>boolean</i> indicating whether we are looking for a file within a collection of not. If <i>true</i> then the tree paths head in the collection name, and no element in the path refers to the import directory. Otherwise the paths head will be one of the system roots and all traversed file locations will exist in the path.
     155     * @return A <strong>TreePath</strong> which traverses the file system tree to the specified file.
     156     */
     157    public static TreePath createTreePath(File file, boolean in_col) {
     158    TreePath path = null;
     159    // Get the absolute path of the file.
     160    String abs_path = file.getAbsolutePath();
     161    while(file != null) {
    162162                // If we are looking for a node within our collection, we expect
    163163                // its path from root to be <col_name>/... without any higher
    164164                // details and without gimport. So if we encounter a gimport we
    165165                // skip to its parent, add that, then return.
    166                 if(in_col && file.getName().equals("gimport")) {
    167                      file = file.getParentFile();
    168                      if(path == null) {
    169                           path = new TreePath(file.getName());
    170                      }
    171                      else {
    172                           path = path.pathByAddingChild(file.getName());
    173                      }
    174                      file = null;
    175                 }
    176                 else {
    177                      if(path == null) {
    178                           path = new TreePath(file.getName());
    179                      }
    180                      else {
    181                           path = path.pathByAddingChild(file.getName());
    182                      }
    183                      file = file.getParentFile();
    184                 }
    185           }
    186           // Unfortunately we've created the path in reverse order so we have to
    187           // reverse it.
    188           Object temp[] = new Object[path.getPathCount()];
    189           for(int i = 0; i < temp.length; i++) {
    190                 temp[(temp.length - 1) - i] = path.getPathComponent(i);
    191           }
    192           return new TreePath(temp);
    193     }
    194     /** Decodes a string of text so its safe to use in a Greenstone configuration file. Esentially replaces "\n" with a newline.
    195       * @param raw The <strong>String</strong> before decoding, read from the configuration file..
    196       * @return A <strong>String</strong> ready to be placed in a component.
    197       */
    198     static public String decodeGreenstone(String raw) {
    199           raw = raw.replaceAll("&apos;", "\'");
    200           raw = raw.replaceAll("&gt;", ">");
    201           raw = raw.replaceAll("&lt;", "<");
    202           raw = raw.replaceAll("&quot;", "\"");
    203           raw = raw.replaceAll("&#39;", "\'");
    204           raw = raw.replaceAll("\\\\n", "\n");
    205           return raw;
    206     }
    207     /** Takes a rfc2616 'safe' String and translates it back into its 'unsafe' form. Basically the native c wget decode_string() function, but without pointer stuff. If searches through the String looking for the pattern %xy where x and y are hexidecimal digits and where xy maps to a character.<BR> If x or y are not hexidecimal or % is followed by a \0 then the pattern is left as is.
    208       * @param encoded The url-safe <strong>String</strong> to be decoded.
    209       * @return The decoded <strong>String</strong>.
    210       */
    211     public static String decodeString(String encoded) {
    212           String decoded = "";
    213           for(int i = 0; i < encoded.length(); i++) {
    214                 if(encoded.charAt(i) == '%') {
    215                      if(hexidecimal(encoded.charAt(i+1)) != -1
    216                         && hexidecimal(encoded.charAt(i+2)) != -1) {
    217                           char unsafe_chr = (char)
    218                                 ((hexidecimal(encoded.charAt(i+1)) * 16) +
    219                                 hexidecimal(encoded.charAt(i+2)));
    220                           decoded = decoded + unsafe_chr;
    221                           i = i + 2;
    222                      }
    223                 }
    224                 else {
    225                      decoded = decoded + encoded.charAt(i);
    226                 }
    227           }
    228           return decoded;
    229     }
    230     /** 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.
    231       * @param file The <strong>File</strong> you want to delete.
    232       * @return A <i>boolean</i> which is <i>true</i> if the file specified was successfully deleted, <i>false</i> otherwise.
    233       */
    234     static public boolean delete(File file) {
    235           boolean result = true;
    236           // If files a directory, delete files children.
    237           if(file.isDirectory()) {
    238                 File files[] = file.listFiles();
    239                 for(int i = 0; files != null && result && i < files.length; i++) {
    240                      result = delete(files[i]);
    241                 }
    242           }
    243           if(result) {
     166        if(in_col && file.getName().equals("gimport")) {
     167        file = file.getParentFile();
     168        if(path == null) {
     169            path = new TreePath(file.getName());
     170        }
     171        else {
     172            path = path.pathByAddingChild(file.getName());
     173        }
     174        file = null;
     175        }
     176        else {
     177        if(path == null) {
     178            path = new TreePath(file.getName());
     179        }
     180        else {
     181            path = path.pathByAddingChild(file.getName());
     182        }
     183        file = file.getParentFile();
     184        }
     185    }
     186    // Unfortunately we've created the path in reverse order so we have to
     187    // reverse it.
     188    Object temp[] = new Object[path.getPathCount()];
     189    for(int i = 0; i < temp.length; i++) {
     190        temp[(temp.length - 1) - i] = path.getPathComponent(i);
     191    }
     192    return new TreePath(temp);
     193    }
     194    /** Decodes a string of text so its safe to use in a Greenstone configuration file. Esentially replaces "\n" with a newline.
     195     * @param raw The <strong>String</strong> before decoding, read from the configuration file..
     196     * @return A <strong>String</strong> ready to be placed in a component.
     197     */
     198    static public String decodeGreenstone(String raw) {
     199    raw = raw.replaceAll("&apos;", "\'");
     200    raw = raw.replaceAll("&gt;", ">");
     201    raw = raw.replaceAll("&lt;", "<");
     202    raw = raw.replaceAll("&quot;", "\"");
     203    raw = raw.replaceAll("&#39;", "\'");
     204    raw = raw.replaceAll("\\\\n", "\n");
     205    return raw;
     206    }
     207    /** Takes a rfc2616 'safe' String and translates it back into its 'unsafe' form. Basically the native c wget decode_string() function, but without pointer stuff. If searches through the String looking for the pattern %xy where x and y are hexidecimal digits and where xy maps to a character.<BR> If x or y are not hexidecimal or % is followed by a \0 then the pattern is left as is.
     208     * @param encoded The url-safe <strong>String</strong> to be decoded.
     209     * @return The decoded <strong>String</strong>.
     210     */
     211    public static String decodeString(String encoded) {
     212    String decoded = "";
     213    for(int i = 0; i < encoded.length(); i++) {
     214        if(encoded.charAt(i) == '%') {
     215        if(hexidecimal(encoded.charAt(i+1)) != -1
     216          && hexidecimal(encoded.charAt(i+2)) != -1) {
     217            char unsafe_chr = (char)
     218            ((hexidecimal(encoded.charAt(i+1)) * 16) +
     219            hexidecimal(encoded.charAt(i+2)));
     220            decoded = decoded + unsafe_chr;
     221            i = i + 2;
     222        }
     223        }
     224        else {
     225        decoded = decoded + encoded.charAt(i);
     226        }
     227    }
     228    return decoded;
     229    }
     230    /** 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.
     231     * @param file The <strong>File</strong> you want to delete.
     232     * @return A <i>boolean</i> which is <i>true</i> if the file specified was successfully deleted, <i>false</i> otherwise.
     233     */
     234    static public boolean delete(File file) {
     235    boolean result = true;
     236    // If files a directory, delete files children.
     237    if(file.isDirectory()) {
     238        File files[] = file.listFiles();
     239        for(int i = 0; files != null && result && i < files.length; i++) {
     240        result = delete(files[i]);
     241        }
     242    }
     243    if(result) {
    244244                // Delete file.
    245                 return file.delete();
    246           }
    247           return result;
    248     }
    249     /** Generate a depth first enumeration of a tree. */
    250     static public EnumeratedVector depthFirstEnumeration(TreeNode node, EnumeratedVector result) {
    251           result.add(node);
    252           for(int i = 0; i < node.getChildCount(); i++) {
    253                 depthFirstEnumeration(node.getChildAt(i), result);
    254           }
    255           return result;
    256     }
    257     /** Encodes a string of text so its safe to use in a Greenstone configuration file. Esentially replaces newlines with their escaped form.
    258       * @param raw The <strong>String</strong> before encoding.
    259       * @return A <strong>String</strong> which is safe to write to the configuration file.
    260       */
    261     static public String encodeGreenstone(String raw) {
    262           raw = raw.replaceAll("<", "&lt;");
    263           raw = raw.replaceAll(">", "&gt;");
    264           return raw.replaceAll("\n", "\\\\n");
    265     }
    266 
    267     /** Using this method we can request that a certain document be written, as valid XML, to a certain output stream. This makes use of the Xerces Serialization suite, which should in no way be confused with the usual method of Serialization used by Java. */
    268     static public boolean export(Document document, String filename) {
    269           return export(document, new File(filename));
    270     }
    271 
    272     static public boolean export(Document document, File file) {
    273           try {
    274                 OutputStream os = new FileOutputStream(file);
     245        return file.delete();
     246    }
     247    return result;
     248    }
     249    /** Generate a depth first enumeration of a tree. */
     250    static public EnumeratedVector depthFirstEnumeration(TreeNode node, EnumeratedVector result) {
     251    result.add(node);
     252    for(int i = 0; i < node.getChildCount(); i++) {
     253        depthFirstEnumeration(node.getChildAt(i), result);
     254    }
     255    return result;
     256    }
     257    /** Encodes a string of text so its safe to use in a Greenstone configuration file. Esentially replaces newlines with their escaped form.
     258     * @param raw The <strong>String</strong> before encoding.
     259     * @return A <strong>String</strong> which is safe to write to the configuration file.
     260     */
     261    static public String encodeGreenstone(String raw) {
     262    raw = raw.replaceAll("<", "&lt;");
     263    raw = raw.replaceAll(">", "&gt;");
     264    return raw.replaceAll("\n", "\\\\n");
     265    }
     266
     267    /** Using this method we can request that a certain document be written, as valid XML, to a certain output stream. This makes use of the Xerces Serialization suite, which should in no way be confused with the usual method of Serialization used by Java. */
     268    static public boolean export(Document document, String filename) {
     269    return export(document, new File(filename));
     270    }
     271
     272    static public boolean export(Document document, File file) {
     273    try {
     274        OutputStream os = new FileOutputStream(file);
    275275                // Create an output format for our document.
    276                 OutputFormat f = new OutputFormat(document);
    277                 f.setIndenting(true);
    278                 f.setLineWidth(256);
    279                 f.setPreserveSpace(false);
     276        OutputFormat f = new OutputFormat(document);
     277        f.setIndenting(true);
     278        f.setLineWidth(256);
     279        f.setPreserveSpace(false);
    280280                // Create the necessary writer stream for serialization.
    281                 OutputStreamWriter osw = new OutputStreamWriter(os);
    282                 Writer w               = new BufferedWriter(osw);
     281        OutputStreamWriter osw = new OutputStreamWriter(os);
     282        Writer w               = new BufferedWriter(osw);
    283283                // Generate a new serializer from the above.
    284                 XMLSerializer s        = new XMLSerializer(w, f);
    285                 s.asDOMSerializer();
     284        XMLSerializer s        = new XMLSerializer(w, f);
     285        s.asDOMSerializer();
    286286                // Finally serialize the document to file.
    287                 s.serialize(document);
     287        s.serialize(document);
    288288                // And close.
    289                 os.close();
    290                 return true;
    291           }
    292           // A file not found exception is most likely thrown because the directory the metadata.xml file is attempting to be written to no longer has any files in it. I'll add a test in GDMDocument to test for this, but if it still happens ignore it (a non-existant directory can't really have metadata added to it any way.
    293           catch (FileNotFoundException fnf_exception) {
    294                 if(!file.getName().endsWith(METADATA_XML)) {
    295                      fnf_exception.printStackTrace();
    296                      return false;
    297                 }
    298                 return true;
    299           }
    300           catch (IOException ioe) {
    301                 ioe.printStackTrace();
    302                 return false;
    303           }
    304     }
    305 
    306     /** Given a starting directory, searches for the collect.cfg file and returns it if found.
    307       * @return The collect.cfg File or null if not found.
    308       */
    309     static final public File findConfigFile(File start) {
    310           if(start == null) {
    311                 return null;
    312           }
    313           // See if the collect.cfg files here.
    314           File collect_cfg = new File(start, "collect.cfg");
    315           if(collect_cfg.exists()) {
    316                 return collect_cfg;
    317           }
    318           // Search for the existance of collect.cfg in a etc directory.
    319           File etc_dir = new File(start, "etc" + File.separator + "collect.cfg");
    320           if(etc_dir.exists()) {
    321                 return etc_dir;
    322           }
    323           // Otherwise search this directories parent if its not null.
    324           return findConfigFile(start.getParentFile());
    325     }
    326 
    327     /** Convert a long, detailing the length of a file in bytes, into a nice human readable string using b, kb, Mb and Gb. */
    328     static final public String BYTE_SUFFIX = " b";
    329     static final public long GIGABYTE = 1024000000l;
    330     static final public String GIGABYTE_SUFFIX = " Gb";
    331     static final public long KILOBYTE =       1024l;
    332     static final public String KILOBYTE_SUFFIX = " kb";
    333     static final public long MEGABYTE =    1024000l;
    334     static final public String MEGABYTE_SUFFIX = " mb";
    335     static final public String formatFileLength(long length) {
    336           StringBuffer result = new StringBuffer("");
    337           float number = 0f;
    338           String suffix = null;
    339           // Determine the floating point number and the suffix (radix) used.
    340           if(length >= GIGABYTE) {
    341                 number = (float) length / (float) GIGABYTE;
    342                 suffix = GIGABYTE_SUFFIX;
    343           }
    344           else if(length >= MEGABYTE) {
    345                 number = (float) length / (float) MEGABYTE;
    346                 suffix = MEGABYTE_SUFFIX;
    347           }
    348           else if(length >= KILOBYTE) {
    349                 number = (float) length / (float) KILOBYTE;
    350                 suffix = KILOBYTE_SUFFIX;
    351           }
    352           else {
    353                 number = (float) length;
    354                 suffix = BYTE_SUFFIX;
    355           }
    356           // 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.
    357           String number_str = Float.toString(number);
    358           char number_char[] = number_str.toCharArray();
    359           int pos = 0;
    360           // Print the characters up to the '.'
    361           while(number_char != null && pos < number_char.length && number_char[pos] != '.') {
    362                 result.append(number_char[pos]);
    363                 pos++;
    364           }
    365           if(pos < number_char.length) {
     289        os.close();
     290        return true;
     291    }
     292    // A file not found exception is most likely thrown because the directory the metadata.xml file is attempting to be written to no longer has any files in it. I'll add a test in GDMDocument to test for this, but if it still happens ignore it (a non-existant directory can't really have metadata added to it any way.
     293    catch (FileNotFoundException fnf_exception) {
     294        if(!file.getName().endsWith(METADATA_XML)) {
     295        fnf_exception.printStackTrace();
     296        return false;
     297        }
     298        return true;
     299    }
     300    catch (IOException ioe) {
     301        ioe.printStackTrace();
     302        return false;
     303    }
     304    }
     305
     306    /** Given a starting directory, searches for the collect.cfg file and returns it if found.
     307     * @return The collect.cfg File or null if not found.
     308     */
     309    static final public File findConfigFile(File start) {
     310    if(start == null) {
     311        return null;
     312    }
     313    // See if the collect.cfg files here.
     314    File collect_cfg = new File(start, "collect.cfg");
     315    if(collect_cfg.exists()) {
     316        return collect_cfg;
     317    }
     318    // Search for the existance of collect.cfg in a etc directory.
     319    File etc_dir = new File(start, "etc" + File.separator + "collect.cfg");
     320    if(etc_dir.exists()) {
     321        return etc_dir;
     322    }
     323    // Otherwise search this directories parent if its not null.
     324    return findConfigFile(start.getParentFile());
     325    }
     326
     327    /** Convert a long, detailing the length of a file in bytes, into a nice human readable string using b, kb, Mb and Gb. */
     328    static final public String BYTE_SUFFIX = " b";
     329    static final public long GIGABYTE = 1024000000l;
     330    static final public String GIGABYTE_SUFFIX = " Gb";
     331    static final public long KILOBYTE =       1024l;
     332    static final public String KILOBYTE_SUFFIX = " kb";
     333    static final public long MEGABYTE =    1024000l;
     334    static final public String MEGABYTE_SUFFIX = " mb";
     335    static final public String formatFileLength(long length) {
     336    StringBuffer result = new StringBuffer("");
     337    float number = 0f;
     338    String suffix = null;
     339    // Determine the floating point number and the suffix (radix) used.
     340    if(length >= GIGABYTE) {
     341        number = (float) length / (float) GIGABYTE;
     342        suffix = GIGABYTE_SUFFIX;
     343    }
     344    else if(length >= MEGABYTE) {
     345        number = (float) length / (float) MEGABYTE;
     346        suffix = MEGABYTE_SUFFIX;
     347    }
     348    else if(length >= KILOBYTE) {
     349        number = (float) length / (float) KILOBYTE;
     350        suffix = KILOBYTE_SUFFIX;
     351    }
     352    else {
     353        number = (float) length;
     354        suffix = BYTE_SUFFIX;
     355    }
     356    // 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.
     357    String number_str = Float.toString(number);
     358    char number_char[] = number_str.toCharArray();
     359    int pos = 0;
     360    // Print the characters up to the '.'
     361    while(number_char != null && pos < number_char.length && number_char[pos] != '.') {
     362        result.append(number_char[pos]);
     363        pos++;
     364    }
     365    if(pos < number_char.length) {
    366366                // Print the '.' and at most two characters after it
    367                 result.append(number_char[pos]);
    368                 pos++;
    369                 for(int i = 0; i < 2 && pos < number_char.length; i++, pos++) {
    370                      result.append(number_char[pos]);
    371                 }
     367        result.append(number_char[pos]);
     368        pos++;
     369        for(int i = 0; i < 2 && pos < number_char.length; i++, pos++) {
     370        result.append(number_char[pos]);
     371        }
    372372                // Search through the remaining string for 'E'
    373                 while(pos < number_char.length && number_char[pos] != 'E') {
    374                      pos++;
    375                 }
     373        while(pos < number_char.length && number_char[pos] != 'E') {
     374        pos++;
     375        }
    376376                // If we still have string then we found an E. Copy the remaining string.
    377                 while(pos < number_char.length) {
    378                      result.append(number_char[pos]);
    379                      pos++;
    380                 }
    381           }
    382           // Add suffix
    383           result.append(suffix);
    384           // Done
    385           return result.toString();
    386     }
    387 
    388     /** This method formats a given string, using HTML markup, so its width does not exceed the given width and its appearance if justified.
    389       * @param text The <strong>String</strong> requiring formatting.
    390       * @param width The maximum width per line as an <i>int</i>.
    391       * @return A <strong>String</strong> formatted so as to have no line longer than the specified width.
    392       * 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.
    393       */
    394     static public String formatHTMLWidth(String text, int width) {
    395           HTMLStringTokenizer html = new HTMLStringTokenizer(text);
    396           int current_width = 0;
    397           int threshold = width / 2;
    398           Stack lines = new Stack();
    399           String line = "";
    400           while(html.hasMoreTokens()) {
    401                 String token = html.nextToken();
    402                 while(token != null) {
    403                      if(html.isTag()) {
    404                           // Insert smart HTML tag code here.
    405                           token = null;
    406                      }
    407                      else {
    408                           // If the token is bigger than two thirds width, before we've even started break it down.
    409                           if(current_width + 1 + token.length() > width && token.length() > threshold) {
    410                                 String prefix = token.substring(0, width - 1 - current_width);
    411                                 token = token.substring(prefix.length());
    412                                 if(current_width == 0) {
    413                                     line = line + prefix;
    414                                 }
    415                                 else {
    416                                     line = line + " " + prefix;
    417                                 }
    418                                 lines.push(line);
    419                                 line = "";
    420                                 current_width = 0;
    421                           }
    422                           // If adding the next token would push us over the maximum line width.
    423                           else if(current_width + 1 + token.length() > width) {
    424                                 line = space(line, width, current_width);
    425                                 lines.push(line);
    426                                line = token;
    427                                 current_width = token.length();
    428                                 token = null;
    429                           }
    430                           // Otherwise we should be able to just add the token, give or take.
    431                           else {
    432                                 if(current_width == 0) {
    433                                     line = line + token;
    434                                     current_width = token.length();
    435                                 }
    436                                 else {
    437                                     // Special case for standard punctuation which may exist after a tag like so:
    438                                     // My name is <scratchy>Slim Shady</scratchy>.   <-- Annoying punctuation.
    439                                     if(token.equals(".") || token.equals(",") || token.equals("!") || token.equals("?")) {
    440                                           line = line + token;
    441                                           current_width = current_width + 1;
    442                                     }
    443                                     else {
    444                                           line = line + " " + token;
    445                                           current_width = current_width + 1 + token.length();
    446                                     }
    447                                 }
    448                                 token = null;
    449                           }
    450                      }
    451                 }
    452           }
    453           String result = line;
    454           while(!lines.empty()) {
    455                 result = (String)lines.pop() + "<BR>" + result;
    456           }
    457           // Replace ' ' with "&nbsp;"
    458           boolean tag = false;
    459           int pos = 0;
    460           while(pos < result.length()) {
    461                 if(result.charAt(pos) == '<') {
    462                      tag = true;
    463                 }
    464                 else if(result.charAt(pos) == '>') {
    465                      tag = false;
    466                 }
    467                 else if(result.charAt(pos) == ' ' && !tag) {
    468                      String prefix = result.substring(0, pos);
    469                      String suffix = result.substring(pos + 1);
    470                      result = prefix + "&nbsp;" + suffix;
    471                 }
    472                 pos++;
    473           }
    474           result = "<HTML>" + result + "</HTML>";
    475           return result;
    476     }
    477     /** Format the given filename path string so that it is no longer than the given width. If it is wider replace starting directories with ...
    478       * @param key The key <strong>String</Strong> used to retrieve a phrase from the dictionary for this item.
    479       * @param raw The raw filename path <strong>String</strong>.
    480       * @param width The maximum width as an <i>int</i>.
    481       * @return A path <strong>String</strong> no longer than width.
    482       */
    483     static public String formatPath(String key, String raw, int width) {
    484           JLabel label = new JLabel(Gatherer.dictionary.get(key, raw));
    485           int position = -1;
    486           while(label.getPreferredSize().width > width && (position = raw.indexOf(File.separator)) != -1) {
    487                 raw = "..." + raw.substring(position + 1);
    488                 label.setText(Gatherer.dictionary.get(key, raw));
    489           }
    490           if(raw.indexOf(File.separator) == -1 && raw.startsWith("...")) {
    491                 raw = raw.substring(3);
    492           }
    493           return raw;
    494     }
    495 
    496     /** Method which constructs the archive directory given a certain collection.
    497       * @param col_dir The location of the collection directory as a <strong>String</strong>.
    498       * @return The location of the given collections archive directory, also as a <strong>String</strong>.
    499       */
    500     static public String getArchiveDir(String gsdl_path, String col_name) {
    501           return gsdl_path + File.separator + COL_DIR + col_name + File.separator + ARCHIVE_DIR;
    502     }
    503     /** Method which constructs the build directory given a certain collection.
    504       * @param col_dir The location of the collection directory as a <strong>String</strong>.
    505       * @return The location of the given collections build directory, also as a <strong>String</strong>.
    506       */
    507     static public String getBuildDir(String col_dir) {
    508           if(col_dir == null) {
    509                 return BASE_DIR + BUILD_DIR;
    510           }
    511           return col_dir + BUILD_DIR;
    512     }
    513     /** Builds the private cache dir by appending col_dir and 'cache'.
    514       * @param col_dir A String representing the directory path of the current collection.
    515       * @return A String representing the path to the private file cache within the current collection.
    516       */
    517     public static String getCacheDir(String col_dir) {
    518           return col_dir + GCACHE_DIR;
    519     }
    520     /** Method which constructs the collection directory for Greenstone.
    521       * @param gsdl_path The location of the gsdl installation directory as a <strong>String</strong>.
    522       * @return The location of the collection directory, also as a <strong>String</strong>.
    523       */
    524     public static String getCollectionDir(String gsdl_path) {
    525           return gsdl_path + COL_DIR;
    526     }
    527     /** Method which constructs the configuration file given a certain collection.
    528       * @param col_dir The location of the collection directory as a <strong>String</strong>.
    529       * @return The location of the given collections configuration file, also as a <strong>String</strong>.
    530       */
    531     static public String getConfigDir(String col_dir) {
    532           return col_dir + CONFIG_DIR;
    533     }
    534 
    535     static public String getDateString() {
    536           Calendar current = Calendar.getInstance();
    537           String day_name = null;
    538           switch(current.get(Calendar.DAY_OF_WEEK)) {
    539           case Calendar.MONDAY: day_name = "Mon"; break;
    540           case Calendar.TUESDAY: day_name = "Tue"; break;
    541           case Calendar.WEDNESDAY: day_name = "Wed"; break;
    542           case Calendar.THURSDAY: day_name = "Thu"; break;
    543           case Calendar.FRIDAY: day_name = "Fri"; break;
    544           case Calendar.SATURDAY: day_name = "Sat"; break;
    545           case Calendar.SUNDAY: day_name = "Sun"; break;
    546           default: day_name = "";
    547           }
    548           String month_name = null;
    549           switch(current.get(Calendar.MONTH)) {
    550           case Calendar.JANUARY: month_name = "Jan"; break;
    551           case Calendar.FEBRUARY: month_name = "Feb"; break;
    552           case Calendar.MARCH: month_name = "Mar"; break;
    553           case Calendar.APRIL: month_name = "Apr"; break;
    554           case Calendar.MAY: month_name = "May"; break;
    555           case Calendar.JUNE: month_name = "Jun"; break;
    556           case Calendar.JULY: month_name = "Jul"; break;
    557           case Calendar.AUGUST: month_name = "Aug"; break;
    558           case Calendar.SEPTEMBER: month_name = "Sep"; break;
    559           case Calendar.OCTOBER: month_name = "Oct"; break;
    560           case Calendar.NOVEMBER: month_name = "Nov"; break;
    561           case Calendar.DECEMBER: month_name = "Dec"; break;
    562           default: month_name = "";
    563           }
    564           int day = current.get(Calendar.DAY_OF_MONTH);
    565           int hour = current.get(Calendar.HOUR_OF_DAY);
    566           int minute = current.get(Calendar.MINUTE);
    567           int second = current.get(Calendar.SECOND);
    568           int year = current.get(Calendar.YEAR);
    569 
    570           return day_name + " " + 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);
    571     }
    572 
    573     /** Retrieves and formats the depth field of the config file to four characters.
    574       * @param length The length of the desired string as an <i>int</i>.
    575       * @return A <strong>String</strong> representation of the mirroring depth padded to length characters.
    576       */
    577     public static String getDepthString(int length) {
    578           return pad("" + Gatherer.self.config.getInt("mirroring.depth", false), length);
    579     }
    580     /** Method which constructs the etc directory given a certain collection.
    581       * @param col_dir The location of the collection directory as a <strong>String</strong>.
    582       * @return The location of the given collections etc directory, also as a <strong>String</strong>.
    583       */
    584     public static String getEtcDir(String col_dir) {
    585           return col_dir + ETC_DIR;
    586     }
    587     /** Method to retrieve an image icon with the given filename found in classpath or the resouces directory.
    588       *  @return The specified <strong>ImageIcon</strong>, or an error image replacement if no such images exists.
    589       */
    590     static public ImageIcon getImage(String filename) {
    591           ImageIcon image = null;
    592           try {
    593                 image = new ImageIcon(ClassLoader.getSystemResource("images/" + Gatherer.dictionary.get("Version") + File.separator + filename));
    594           }
    595           catch(Exception error_one) {
    596                 try {
    597                      image = new ImageIcon(ClassLoader.getSystemResource("images/" + filename));
    598                 }
    599                 catch (Exception error_two) {
    600                      if(Gatherer.dictionary != null) {
    601                           File locale_image_file = new File(RES_DIR + Gatherer.dictionary.get("Version") + File.separator + filename);
    602                           if(locale_image_file.exists()) {
    603                                 image = new ImageIcon(locale_image_file.toString());
    604                           }
    605                      }
    606                      if(image == null) {
    607                           File image_file = new File(RES_DIR + filename);
    608                           if(image_file.exists()) {
    609                                 image = new ImageIcon(image_file.toString());
    610                           }
    611                      }
    612                 }
    613           }
    614           if(image == null) {
    615                 image = ERROR_ICON;
    616           }
    617           return image;
    618     }
    619 
    620     /** Method which constructs the import directory given a certain collection.
    621       * @param col_dir The location of the collection directory as a <strong>String</strong>.
    622       * @return The location of the given collections import directory, also as a <strong>String</strong>.
    623       */
    624     public static String getImportDir(String col_dir) {
    625           return col_dir + IMPORT_DIR;
    626     }
    627     /** Method which constructs the index directory given a certain collection.
    628       * @param col_dir The location of the collection directory as a <strong>String</strong>.
    629       * @return The location of the given collections index directory, also as a <strong>String</strong>.
    630       */
    631     static public String getIndexDir(String col_dir) {
    632           return col_dir + INDEX_DIR;
    633     }
    634     /** Method which constructs the log directory given a certain collection.
    635       * @param col_dir The location of the collection directory as a <strong>String</strong>.
    636       * @return The location of the given collections log directory, also as a <strong>String</strong>.
    637       */
    638     public static String getLogDir(String col_dir) {
    639           return col_dir + LOG_DIR;
    640     }
    641     /** Determine this machines name.
    642       * @return The name as a <strong>String</strong>.
    643       */
     377        while(pos < number_char.length) {
     378        result.append(number_char[pos]);
     379        pos++;
     380        }
     381    }
     382    // Add suffix
     383    result.append(suffix);
     384    // Done
     385    return result.toString();
     386    }
     387
     388    /** This method formats a given string, using HTML markup, so its width does not exceed the given width and its appearance if justified.
     389     * @param text The <strong>String</strong> requiring formatting.
     390     * @param width The maximum width per line as an <i>int</i>.
     391     * @return A <strong>String</strong> formatted so as to have no line longer than the specified width.
     392     * 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.
     393     */
     394    static public String formatHTMLWidth(String text, int width) {
     395    HTMLStringTokenizer html = new HTMLStringTokenizer(text);
     396    int current_width = 0;
     397    int threshold = width / 2;
     398    Stack lines = new Stack();
     399    String line = "";
     400    while(html.hasMoreTokens()) {
     401        String token = html.nextToken();
     402        while(token != null) {
     403        if(html.isTag()) {
     404            // Insert smart HTML tag code here.
     405            token = null;
     406        }
     407        else {
     408            // If the token is bigger than two thirds width, before we've even started break it down.
     409            if(current_width + 1 + token.length() > width && token.length() > threshold) {
     410            String prefix = token.substring(0, width - 1 - current_width);
     411            token = token.substring(prefix.length());
     412            if(current_width == 0) {
     413                line = line + prefix;
     414            }
     415            else {
     416                line = line + " " + prefix;
     417            }
     418            lines.push(line);
     419            line = "";
     420            current_width = 0;
     421            }
     422            // If adding the next token would push us over the maximum line width.
     423            else if(current_width + 1 + token.length() > width) {
     424            line = space(line, width, current_width);
     425            lines.push(line);
     426            line = token;
     427            current_width = token.length();
     428            token = null;
     429            }
     430            // Otherwise we should be able to just add the token, give or take.
     431            else {
     432            if(current_width == 0) {
     433                line = line + token;
     434                current_width = token.length();
     435            }
     436            else {
     437                // Special case for standard punctuation which may exist after a tag like so:
     438                // My name is <scratchy>Slim Shady</scratchy>.   <-- Annoying punctuation.
     439                if(token.equals(".") || token.equals(",") || token.equals("!") || token.equals("?")) {
     440                line = line + token;
     441                current_width = current_width + 1;
     442                }
     443                else {
     444                line = line + " " + token;
     445                current_width = current_width + 1 + token.length();
     446                }
     447            }
     448            token = null;
     449            }
     450        }
     451        }
     452    }
     453    String result = line;
     454    while(!lines.empty()) {
     455        result = (String)lines.pop() + "<BR>" + result;
     456    }
     457    // Replace ' ' with "&nbsp;"
     458    boolean tag = false;
     459    int pos = 0;
     460    while(pos < result.length()) {
     461        if(result.charAt(pos) == '<') {
     462        tag = true;
     463        }
     464        else if(result.charAt(pos) == '>') {
     465        tag = false;
     466        }
     467        else if(result.charAt(pos) == ' ' && !tag) {
     468        String prefix = result.substring(0, pos);
     469        String suffix = result.substring(pos + 1);
     470        result = prefix + "&nbsp;" + suffix;
     471        }
     472        pos++;
     473    }
     474    result = "<HTML>" + result + "</HTML>";
     475    return result;
     476    }
     477    /** Format the given filename path string so that it is no longer than the given width. If it is wider replace starting directories with ...
     478     * @param key The key <strong>String</Strong> used to retrieve a phrase from the dictionary for this item.
     479     * @param raw The raw filename path <strong>String</strong>.
     480     * @param width The maximum width as an <i>int</i>.
     481     * @return A path <strong>String</strong> no longer than width.
     482     */
     483    static public String formatPath(String key, String raw, int width) {
     484    JLabel label = new JLabel(Gatherer.dictionary.get(key, raw));
     485    int position = -1;
     486    while(label.getPreferredSize().width > width && (position = raw.indexOf(File.separator)) != -1) {
     487        raw = "..." + raw.substring(position + 1);
     488        label.setText(Gatherer.dictionary.get(key, raw));
     489    }
     490    if(raw.indexOf(File.separator) == -1 && raw.startsWith("...")) {
     491        raw = raw.substring(3);
     492    }
     493    return raw;
     494    }
     495
     496    /** Method which constructs the archive directory given a certain collection.
     497     * @param col_dir The location of the collection directory as a <strong>String</strong>.
     498     * @return The location of the given collections archive directory, also as a <strong>String</strong>.
     499     */
     500    static public String getArchiveDir(String gsdl_path, String col_name) {
     501    return gsdl_path + File.separator + COL_DIR + col_name + File.separator + ARCHIVE_DIR;
     502    }
     503    /** Method which constructs the build directory given a certain collection.
     504     * @param col_dir The location of the collection directory as a <strong>String</strong>.
     505     * @return The location of the given collections build directory, also as a <strong>String</strong>.
     506     */
     507    static public String getBuildDir(String col_dir) {
     508    if(col_dir == null) {
     509        return BASE_DIR + BUILD_DIR;
     510    }
     511    return col_dir + BUILD_DIR;
     512    }
     513    /** Builds the private cache dir by appending col_dir and 'cache'.
     514     * @param col_dir A String representing the directory path of the current collection.
     515     * @return A String representing the path to the private file cache within the current collection.
     516     */
     517    public static String getCacheDir(String col_dir) {
     518    return col_dir + GCACHE_DIR;
     519    }
     520    /** Method which constructs the collection directory for Greenstone.
     521     * @param gsdl_path The location of the gsdl installation directory as a <strong>String</strong>.
     522     * @return The location of the collection directory, also as a <strong>String</strong>.
     523     */
     524    public static String getCollectionDir(String gsdl_path) {
     525    return gsdl_path + COL_DIR;
     526    }
     527    /** Method which constructs the configuration file given a certain collection.
     528     * @param col_dir The location of the collection directory as a <strong>String</strong>.
     529     * @return The location of the given collections configuration file, also as a <strong>String</strong>.
     530     */
     531    static public String getConfigDir(String col_dir) {
     532    return col_dir + CONFIG_DIR;
     533    }
     534
     535    static public String getDateString() {
     536    Calendar current = Calendar.getInstance();
     537    String day_name = null;
     538    switch(current.get(Calendar.DAY_OF_WEEK)) {
     539    case Calendar.MONDAY: day_name = "Mon"; break;
     540    case Calendar.TUESDAY: day_name = "Tue"; break;
     541    case Calendar.WEDNESDAY: day_name = "Wed"; break;
     542    case Calendar.THURSDAY: day_name = "Thu"; break;
     543    case Calendar.FRIDAY: day_name = "Fri"; break;
     544    case Calendar.SATURDAY: day_name = "Sat"; break;
     545    case Calendar.SUNDAY: day_name = "Sun"; break;
     546    default: day_name = "";
     547    }
     548    String month_name = null;
     549    switch(current.get(Calendar.MONTH)) {
     550    case Calendar.JANUARY: month_name = "Jan"; break;
     551    case Calendar.FEBRUARY: month_name = "Feb"; break;
     552    case Calendar.MARCH: month_name = "Mar"; break;
     553    case Calendar.APRIL: month_name = "Apr"; break;
     554    case Calendar.MAY: month_name = "May"; break;
     555    case Calendar.JUNE: month_name = "Jun"; break;
     556    case Calendar.JULY: month_name = "Jul"; break;
     557    case Calendar.AUGUST: month_name = "Aug"; break;
     558    case Calendar.SEPTEMBER: month_name = "Sep"; break;
     559    case Calendar.OCTOBER: month_name = "Oct"; break;
     560    case Calendar.NOVEMBER: month_name = "Nov"; break;
     561    case Calendar.DECEMBER: month_name = "Dec"; break;
     562    default: month_name = "";
     563    }
     564    int day = current.get(Calendar.DAY_OF_MONTH);
     565    int hour = current.get(Calendar.HOUR_OF_DAY);
     566    int minute = current.get(Calendar.MINUTE);
     567    int second = current.get(Calendar.SECOND);
     568    int year = current.get(Calendar.YEAR);
     569
     570    return day_name + " " + 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);
     571    }
     572
     573    /** Retrieves and formats the depth field of the config file to four characters.
     574     * @param length The length of the desired string as an <i>int</i>.
     575     * @return A <strong>String</strong> representation of the mirroring depth padded to length characters.
     576     */
     577    public static String getDepthString(int length) {
     578    return pad("" + Gatherer.self.config.getInt("mirroring.depth", false), length);
     579    }
     580    /** Method which constructs the etc directory given a certain collection.
     581     * @param col_dir The location of the collection directory as a <strong>String</strong>.
     582     * @return The location of the given collections etc directory, also as a <strong>String</strong>.
     583     */
     584    public static String getEtcDir(String col_dir) {
     585    return col_dir + ETC_DIR;
     586    }
     587    /** Method to retrieve an image icon with the given filename found in classpath or the resouces directory.
     588     *  @return The specified <strong>ImageIcon</strong>, or an error image replacement if no such images exists.
     589     */
     590    static public ImageIcon getImage(String filename) {
     591    ImageIcon image = null;
     592    try {
     593        image = new ImageIcon(ClassLoader.getSystemResource("images/" + Gatherer.dictionary.get("Version") + File.separator + filename));
     594    }
     595    catch(Exception error_one) {
     596        try {
     597        image = new ImageIcon(ClassLoader.getSystemResource("images/" + filename));
     598        }
     599        catch (Exception error_two) {
     600        if(Gatherer.dictionary != null) {
     601            File locale_image_file = new File(RES_DIR + Gatherer.dictionary.get("Version") + File.separator + filename);
     602            if(locale_image_file.exists()) {
     603            image = new ImageIcon(locale_image_file.toString());
     604            }
     605        }
     606        if(image == null) {
     607            File image_file = new File(RES_DIR + filename);
     608            if(image_file.exists()) {
     609            image = new ImageIcon(image_file.toString());
     610            }
     611        }
     612        }
     613    }
     614    if(image == null) {
     615        image = ERROR_ICON;
     616    }
     617    return image;
     618    }
     619
     620    /** Method which constructs the import directory given a certain collection.
     621     * @param col_dir The location of the collection directory as a <strong>String</strong>.
     622     * @return The location of the given collections import directory, also as a <strong>String</strong>.
     623     */
     624    public static String getImportDir(String col_dir) {
     625    return col_dir + IMPORT_DIR;
     626    }
     627    /** Method which constructs the index directory given a certain collection.
     628     * @param col_dir The location of the collection directory as a <strong>String</strong>.
     629     * @return The location of the given collections index directory, also as a <strong>String</strong>.
     630     */
     631    static public String getIndexDir(String col_dir) {
     632    return col_dir + INDEX_DIR;
     633    }
     634    /** Method which constructs the log directory given a certain collection.
     635     * @param col_dir The location of the collection directory as a <strong>String</strong>.
     636     * @return The location of the given collections log directory, also as a <strong>String</strong>.
     637     */
     638    public static String getLogDir(String col_dir) {
     639    return col_dir + LOG_DIR;
     640    }
     641    /** Determine this machines name.
     642     * @return The name as a <strong>String</strong>.
     643     */
    644644    static public String getMachineName() {
    645           try {
    646                 return InetAddress.getLocalHost().getHostName();
    647           }
    648           catch(UnknownHostException ex) {
    649           }
    650           return "Unknown Machine";
    651     }
    652     /** Method which constructs the metadata directory given a certain collection.
    653       * @param col_dir The location of the collection directory as a <strong>String</strong>.
    654       * @return The location of the given collections metadata directory, also as a <strong>String</strong>.
    655       */
    656     static public String getMetadataDir(String col_dir) {
    657           return col_dir + META_DIR;
    658     }
    659 
    660 
    661     static public File getRecycleDirectory() {
    662           return new File(RECYCLE);
    663     }
    664 
    665     /** Determine whether a character is a hexidecimal one.
    666       * @param chr The <i>char</i> in question.
    667       * @return An <i>int</i> representing the value of the hexidecimal character or -1 if not a hexidecimal.
    668       */
    669     public static int hexidecimal(char chr) {
    670           switch(chr) {
    671           case '0':
    672                 return 0;
    673           case '1':
    674                 return 1;
    675           case '2':
    676                 return 2;
    677           case '3':
    678                 return 3;
    679           case '4':
    680                 return 4;
    681           case '5':
    682                 return 5;
    683           case '6':
    684                 return 6;
    685           case '7':
    686                 return 7;
    687           case '8':
    688                 return 8;
    689           case '9':
    690                 return 9;
    691           case 'A':
    692                 return 10;
    693           case 'B':
    694                 return 11;
    695           case 'C':
    696                 return 12;
    697           case 'D':
    698                 return 13;
    699           case 'E':
    700                 return 14;
    701           case 'F':
    702                 return 15;
    703           default:
    704                 return -1;
    705           }
    706     }
    707 
    708     /** A string is a valid hierarchy index if it matches '[0-9](\.[0-9])*' */
    709     static public boolean isIndex(String raw) {
    710           boolean result = true;
    711           for(int i = 0; result && i < raw.length(); i++) {
    712                 char c = raw.charAt(i);
    713                 if(Character.isDigit(c) || (c == '.' && (i != 0 || i != raw.length() - 1))) {
    714                      // Valid index
    715                 }
    716                 else {
    717                      result = false;
    718                 }
    719           }
    720           return result;
    721     }
    722 
    723     /** Method to determine if the host system is Microsoft Windows based.
    724       * @return A <i>boolean</i> which is <i>true</i> if the platform is Windows, <i>false</i> otherwise.
    725       */
    726     public static boolean isWindows() {
    727           Properties props = System.getProperties();
    728           String os_name = props.getProperty("os.name","");
    729           if(os_name.startsWith("Windows")) {
    730                 return true;
    731           }
    732           return false;
    733     }
    734     /** Takes a string and a desired length and pads out the string to the length by adding spaces to the left.
    735       * @param str The target <strong>String</strong> that needs to be padded.
    736       * @param length The desired length of the string as an <i>int</i>.
    737       * @return A <strong>String</strong> made from appending space characters with the string until it has a length equal to length.
    738       */
    739     public static String pad(String str, int length) {
    740           return pad(str, length, ' ', true);
    741     }
    742     public static String pad(String str_raw, int length, char fill, boolean end) {
    743           StringBuffer str = new StringBuffer(str_raw);
    744           while(str.length() < length) {
    745                 if(end) {
    746                      str.insert(0, fill);
    747                 }
    748                 else {
    749                      str.append(fill);
    750                 }
    751           }
    752           return str.toString();
    753     }
    754 
    755     /** 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. */
    756     static public Document parse(String filename, boolean use_classloader) {
    757           File file = null;
    758           if(use_classloader) {
    759                 try {
    760                      URL url = ClassLoader.getSystemResource(filename);
    761                      file = new File(URLDecoder.decode(url.getFile()));
    762                      url = null;
    763                 }
    764                 catch (Exception error) {
    765                      // Most likely file name.
    766                      file = new File("classes" + File.separator + filename);
    767                      //Gatherer.printStackTrace(error);
    768                 }
    769           }
    770           if(file == null) {
    771                 file = new File(filename);
    772           }
    773           return parse(file, true);
    774     }
    775     /** Parse in a xml document from a given file. */
    776     static public Document parse(File file) {
    777           return parse(file, true);
    778     }
    779     /** Parse in a xml document from a given file. */
    780     static public Document parse(File file, boolean noisey) {
    781           Document document = null;
    782           try {
    783                 FileInputStream fis   = new FileInputStream(file);
    784                 InputStreamReader isr = new InputStreamReader(fis);
    785                 Reader r              = new BufferedReader(isr);
    786                 InputSource isc       = new InputSource(r);
    787                 DOMParser parser      = new DOMParser();
    788                 parser.setFeature("http://xml.org/sax/features/validation", false);
    789                 parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
     645    try {
     646        return InetAddress.getLocalHost().getHostName();
     647    }
     648    catch(UnknownHostException ex) {
     649    }
     650    return "Unknown Machine";
     651    }
     652    /** Method which constructs the metadata directory given a certain collection.
     653     * @param col_dir The location of the collection directory as a <strong>String</strong>.
     654     * @return The location of the given collections metadata directory, also as a <strong>String</strong>.
     655     */
     656    static public String getMetadataDir(String col_dir) {
     657    return col_dir + META_DIR;
     658    }
     659
     660
     661    static public File getRecycleDirectory() {
     662    return new File(RECYCLE);
     663    }
     664
     665    /** Determine whether a character is a hexidecimal one.
     666     * @param chr The <i>char</i> in question.
     667     * @return An <i>int</i> representing the value of the hexidecimal character or -1 if not a hexidecimal.
     668     */
     669    public static int hexidecimal(char chr) {
     670    switch(chr) {
     671    case '0':
     672        return 0;
     673    case '1':
     674        return 1;
     675    case '2':
     676        return 2;
     677    case '3':
     678        return 3;
     679    case '4':
     680        return 4;
     681    case '5':
     682        return 5;
     683    case '6':
     684        return 6;
     685    case '7':
     686        return 7;
     687    case '8':
     688        return 8;
     689    case '9':
     690        return 9;
     691    case 'A':
     692        return 10;
     693    case 'B':
     694        return 11;
     695    case 'C':
     696        return 12;
     697    case 'D':
     698        return 13;
     699    case 'E':
     700        return 14;
     701    case 'F':
     702        return 15;
     703    default:
     704        return -1;
     705    }
     706    }
     707
     708    /** A string is a valid hierarchy index if it matches '[0-9](\.[0-9])*' */
     709    static public boolean isIndex(String raw) {
     710    boolean result = true;
     711    for(int i = 0; result && i < raw.length(); i++) {
     712        char c = raw.charAt(i);
     713        if(Character.isDigit(c) || (c == '.' && (i != 0 || i != raw.length() - 1))) {
     714        // Valid index
     715        }
     716        else {
     717        result = false;
     718        }
     719    }
     720    return result;
     721    }
     722
     723    /** Method to determine if the host system is Microsoft Windows based.
     724     * @return A <i>boolean</i> which is <i>true</i> if the platform is Windows, <i>false</i> otherwise.
     725     */
     726    public static boolean isWindows() {
     727    Properties props = System.getProperties();
     728    String os_name = props.getProperty("os.name","");
     729    if(os_name.startsWith("Windows")) {
     730        return true;
     731    }
     732    return false;
     733    }
     734    /** Takes a string and a desired length and pads out the string to the length by adding spaces to the left.
     735     * @param str The target <strong>String</strong> that needs to be padded.
     736     * @param length The desired length of the string as an <i>int</i>.
     737     * @return A <strong>String</strong> made from appending space characters with the string until it has a length equal to length.
     738     */
     739    public static String pad(String str, int length) {
     740    return pad(str, length, ' ', true);
     741    }
     742    public static String pad(String str_raw, int length, char fill, boolean end) {
     743    StringBuffer str = new StringBuffer(str_raw);
     744    while(str.length() < length) {
     745        if(end) {
     746        str.insert(0, fill);
     747        }
     748        else {
     749        str.append(fill);
     750        }
     751    }
     752    return str.toString();
     753    }
     754
     755    /** 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. */
     756    static public Document parse(String filename, boolean use_classloader) {
     757    File file = null;
     758    if(use_classloader) {
     759        try {
     760        URL url = ClassLoader.getSystemResource(filename);
     761        file = new File(URLDecoder.decode(url.getFile()));
     762        url = null;
     763        }
     764        catch (Exception error) {
     765        // Most likely file name.
     766        file = new File("classes" + File.separator + filename);
     767        //Gatherer.printStackTrace(error);
     768        }
     769    }
     770    if(file == null) {
     771        file = new File(filename);
     772    }
     773    return parse(file, true);
     774    }
     775    /** Parse in a xml document from a given file. */
     776    static public Document parse(File file) {
     777    return parse(file, true);
     778    }
     779    /** Parse in a xml document from a given file. */
     780    static public Document parse(File file, boolean noisey) {
     781    Document document = null;
     782    try {
     783        FileInputStream fis   = new FileInputStream(file);
     784        InputStreamReader isr = new InputStreamReader(fis);
     785        Reader r              = new BufferedReader(isr);
     786        InputSource isc       = new InputSource(r);
     787        DOMParser parser      = new DOMParser();
     788        parser.setFeature("http://xml.org/sax/features/validation", false);
     789        parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
    790790                // May or may not be ignored, the documentation for Xerces is contradictory. If it works then parsing -should- be faster.
    791                 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);
    792                 parser.setFeature("http://apache.org/xml/features/dom/include-ignorable-whitespace", false);
    793                 parser.parse(isc);
    794                 document = parser.getDocument();
    795                 isr.close();
    796                 fis.close();
    797                 parser = null;
    798                 isc = null;
    799                 r = null;
    800                 isr = null;
    801                 fis = null;
    802                 file = null;
    803           }
    804           catch (Exception error) {
    805                 if(noisey) {
    806                      Gatherer.printStackTrace(error);
    807                 }
    808           }
    809           return document;
    810     }
    811 
    812     /** Method to spread out a line of text so that is is justified to the given width, by attempting to widen white-spacing in a balanced way.
    813       * @param original The <strong>String</strong> to justify.
    814       * @param width The desired width as an <i>int</i>.
    815       * @param current_width An <i>int</i> representing the current width of the string, which takes into account special characters.
    816       * @return The newly justified <strong>String</strong>.
    817       */
    818     static public String space(String original, int width, int current_width) {
    819           // Strip trailing whitespace.
    820           while(original.charAt(original.length() - 1) == ' ') {
    821                 original = original.substring(0, original.length() - 2);
    822           }
    823           int diff = width - current_width;
    824           // Now add diff spaces, one at each existing space.
    825           int pos = 0;
    826           while(diff > 0) {
    827                 if(pos == original.length()) {
    828                      pos = 0;
    829                 }
    830                 if(original.charAt(pos) == ' ') {
    831                      // Insert a space.
    832                      String prefix = original.substring(0, pos);
    833                      String suffix = original.substring(pos);
    834                      original = prefix + " " + suffix;
    835                      pos = pos + 2;
    836                      diff--;
    837                 }
    838                 pos++;
    839           }
    840           return original;
    841     }
    842     /** Method to strip new lines and extra spaces from a string. Used to restore text that has been mangled into width formatted blocks by the DOM parser.
    843       * @param raw The <strong>Strong</strong> containing the mangled text.
    844       * @return A <strong>String</strong> with new lines and extra spaces removed.
    845       */
    846     static public String stripNL(String raw_str) {
    847           byte raw[] = raw_str.getBytes();
    848           byte formatted[] = new byte[raw.length];
    849           byte previous = '\0';
    850           int j = 0;
    851           for(int i = 0; i < raw.length; i++) {
    852                 if(raw[i] == '\n') {
    853                      // Skip new lines.
    854                 }
    855                 else if(raw[i] == '\t') {
    856                      // Skip tabs.
    857                 }
    858                 else if(raw[i] == ' ' && raw[i] == previous) {
    859                      // Skip erroneous whitespace.
    860                 }
    861                 else {
    862                      formatted[j] = raw[i];
    863                      j++;
    864                 }
    865                 previous = raw[i];
    866           }
    867           byte finish[] = new byte[j];
    868           System.arraycopy(formatted, 0, finish, 0, j);
    869           return new String(finish);
    870     }
    871     /** Trims the string text to the length specified removing end characters and adding if necessary.
    872       * @param text A <strong>String</strong> which you wish to ensure is shorter than length.
    873       * @param length An <i>int</i> specifying the strings maximum length after which its trimmed.
    874       * @return The trimmed <strong>String</strong>.
    875       */
    876     public static String trim(String text, int length) {
    877           if(text.length() > length) {
    878                 text = text.substring(0, length);
    879                 text = text + "...";
    880           }
    881           return text;
    882     }
    883 
    884     static public String trimCenter(String text, int length) {
    885           if(text.length() > length) {
    886                 int half = (length - 3) / 2;
    887                 StringBuffer temp = new StringBuffer(text.substring(0, half));
    888                 temp.append("...");
    889                 temp.append(text.substring(text.length() - half));
    890                 text = temp.toString();
    891           }
    892           return text;
    893     }
    894     /** This method checks to see what registered file system root directorys are mounted, and returns only accessible ones. The exception is removable media drives (in particular floppy-disk drives) which will throw all sorts of error if we test them here. Instead they are assumed to be always accessible, but a test is conducted at the time you attempt to map them to test for actual accessibility (then at least the errors are thrown after the user tries to initiate the mapping of the drive which has no disk in it).
    895       * @param roots A <strong>File[]</strong> containing all of the file system roots registered on this system.
    896       * @return A filtered <strong>File[]</strong> containing only those drives that are accessible and/or are floppy-disk media drives.
    897       */
    898     public static File[] validateDrives(File roots[]) {
    899           Vector valid = new Vector();
    900           for(int i = 0; i < roots.length; i++) {
    901                 String name = roots[i].getAbsolutePath();
    902                 name = name.toLowerCase();
    903                 if(!name.startsWith("a:") && !name.startsWith("b:")) {
    904                      valid.add(roots[i]);
    905                 }
    906           }
    907           roots = new File[valid.size()];
    908           for(int i = 0; i < roots.length; i++) {
    909                 roots[i] = (File)valid.get(i);
    910           }
    911           return roots;
    912     }
     791        parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);
     792        parser.setFeature("http://apache.org/xml/features/dom/include-ignorable-whitespace", false);
     793        parser.parse(isc);
     794        document = parser.getDocument();
     795        isr.close();
     796        fis.close();
     797        parser = null;
     798        isc = null;
     799        r = null;
     800        isr = null;
     801        fis = null;
     802        file = null;
     803    }
     804    catch (Exception error) {
     805        if(noisey) {
     806        Gatherer.printStackTrace(error);
     807        }
     808    }
     809    return document;
     810    }
     811
     812    /** Method to spread out a line of text so that is is justified to the given width, by attempting to widen white-spacing in a balanced way.
     813     * @param original The <strong>String</strong> to justify.
     814     * @param width The desired width as an <i>int</i>.
     815     * @param current_width An <i>int</i> representing the current width of the string, which takes into account special characters.
     816     * @return The newly justified <strong>String</strong>.
     817     */
     818    static public String space(String original, int width, int current_width) {
     819    // Strip trailing whitespace.
     820    while(original.charAt(original.length() - 1) == ' ') {
     821        original = original.substring(0, original.length() - 2);
     822    }
     823    int diff = width - current_width;
     824    // Now add diff spaces, one at each existing space.
     825    int pos = 0;
     826    while(diff > 0) {
     827        if(pos == original.length()) {
     828        pos = 0;
     829        }
     830        if(original.charAt(pos) == ' ') {
     831        // Insert a space.
     832        String prefix = original.substring(0, pos);
     833        String suffix = original.substring(pos);
     834        original = prefix + " " + suffix;
     835        pos = pos + 2;
     836        diff--;
     837        }
     838        pos++;
     839    }
     840    return original;
     841    }
     842    /** Method to strip new lines and extra spaces from a string. Used to restore text that has been mangled into width formatted blocks by the DOM parser.
     843     * @param raw The <strong>Strong</strong> containing the mangled text.
     844     * @return A <strong>String</strong> with new lines and extra spaces removed.
     845     */
     846    static public String stripNL(String raw_str) {
     847    byte raw[] = raw_str.getBytes();
     848    byte formatted[] = new byte[raw.length];
     849    byte previous = '\0';
     850    int j = 0;
     851    for(int i = 0; i < raw.length; i++) {
     852        if(raw[i] == '\n') {
     853        // Skip new lines.
     854        }
     855        else if(raw[i] == '\t') {
     856        // Skip tabs.
     857        }
     858        else if(raw[i] == ' ' && raw[i] == previous) {
     859        // Skip erroneous whitespace.
     860        }
     861        else {
     862        formatted[j] = raw[i];
     863        j++;
     864        }
     865        previous = raw[i];
     866    }
     867    byte finish[] = new byte[j];
     868    System.arraycopy(formatted, 0, finish, 0, j);
     869    return new String(finish);
     870    }
     871    /** Trims the string text to the length specified removing end characters and adding if necessary.
     872     * @param text A <strong>String</strong> which you wish to ensure is shorter than length.
     873     * @param length An <i>int</i> specifying the strings maximum length after which its trimmed.
     874     * @return The trimmed <strong>String</strong>.
     875     */
     876    public static String trim(String text, int length) {
     877    if(text.length() > length) {
     878        text = text.substring(0, length);
     879        text = text + "...";
     880    }
     881    return text;
     882    }
     883
     884    static public String trimCenter(String text, int length) {
     885    if(text.length() > length) {
     886        int half = (length - 3) / 2;
     887        StringBuffer temp = new StringBuffer(text.substring(0, half));
     888        temp.append("...");
     889        temp.append(text.substring(text.length() - half));
     890        text = temp.toString();
     891    }
     892    return text;
     893    }
     894    /** This method checks to see what registered file system root directorys are mounted, and returns only accessible ones. The exception is removable media drives (in particular floppy-disk drives) which will throw all sorts of error if we test them here. Instead they are assumed to be always accessible, but a test is conducted at the time you attempt to map them to test for actual accessibility (then at least the errors are thrown after the user tries to initiate the mapping of the drive which has no disk in it).
     895     * @param roots A <strong>File[]</strong> containing all of the file system roots registered on this system.
     896     * @return A filtered <strong>File[]</strong> containing only those drives that are accessible and/or are floppy-disk media drives.
     897     */
     898    public static File[] validateDrives(File roots[]) {
     899    Vector valid = new Vector();
     900    for(int i = 0; i < roots.length; i++) {
     901        String name = roots[i].getAbsolutePath();
     902        name = name.toLowerCase();
     903        if(!name.startsWith("a:") && !name.startsWith("b:")) {
     904        valid.add(roots[i]);
     905        }
     906    }
     907    roots = new File[valid.size()];
     908    for(int i = 0; i < roots.length; i++) {
     909        roots[i] = (File)valid.get(i);
     910    }
     911    return roots;
     912    }
    913913}
    914 
    915 
    916 
Note: See TracChangeset for help on using the changeset viewer.