Index: /trunk/gli/src/org/greenstone/gatherer/file/FileAssociationManager.java
===================================================================
--- /trunk/gli/src/org/greenstone/gatherer/file/FileAssociationManager.java	(revision 4669)
+++ /trunk/gli/src/org/greenstone/gatherer/file/FileAssociationManager.java	(revision 4670)
@@ -1,3 +1,2 @@
-package org.greenstone.gatherer.file;
 /**
  *#########################################################################
@@ -7,13 +6,7 @@
  * University of Waikato, New Zealand.
  *
- * <BR><BR>
- *
  * Author: John Thompson, Greenstone Digital Library, University of Waikato
  *
- * <BR><BR>
- *
  * Copyright (C) 1999 New Zealand Digital Library Project
- *
- * <BR><BR>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -22,12 +15,8 @@
  * (at your option) any later version.
  *
- * <BR><BR>
- *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
- * <BR><BR>
  *
  * You should have received a copy of the GNU General Public License
@@ -36,9 +25,7 @@
  *########################################################################
  */
+package org.greenstone.gatherer.file;
+
 import java.io.*;
-import java.lang.*;
-import java.nio.charset.*;
-import java.util.*;
-import javax.swing.*;
 import org.greenstone.gatherer.Gatherer;
 import org.greenstone.gatherer.gui.FileAssociationDialog;
@@ -47,151 +34,167 @@
 import org.greenstone.gatherer.util.WinRegistry;
 import org.w3c.dom.*;
-/** Provides a manager for relating a filetype to a command to open that filetype, and an optional icon. Under windows this will attempt to leverage the Windows Registry. If this fails, or under other platforms, the user will be prompted to enter the required details manually. This information will then be stored for any further sessions of the Gatherer. Of course the ability to edit these settings must also be provided. */
-public class FileAssociationManager 
-    extends HashMap {
-	 
-    static private final File ASSOC_FILE = new File(Utility.BASE_DIR, "associations.xml");
+
+public class FileAssociationManager {
+    static final public String FILENAME_ARG = "%1";
+    static final private String DATA_FILENAME = "associations.xml";
+    static final private String ENTRY_ELEMENT = "Entry";
+    static final private String ESCAPE = "\\\\"; // '\'
+    static final private String ESCAPED_ESCAPE = "\\\\\\\\"; // '\\'
+    static final private String EXTENSION_ATTRIBUTE = "extension";
+    private Document document;
+    private File data_file;   
 
     public FileAssociationManager() {
-	// Attempt to load any previous file associations
-	if(ASSOC_FILE.exists()) {
-	    load();
-	}
-    }
-
-    public void destroy() {
-	save();
-    }
-
-    public String getCommand(File file) {
-	String command = null;
-	String filename = file.toString();
-	// Determine extension
-	int index = -1;
-	if((index = filename.lastIndexOf(".")) != -1) {
-	    String extension = filename.substring(index + 1);
-	    command = getCommand(file, extension);
-	}
-	return command;
-    }
-
-    public String getCommand(File file, String extension) {
-	// We first check to see if we already know the answer to how to run this program.
-	String command = (String) get(extension);
-	if(command != null) {
-	    command = command + " " + file.getAbsolutePath();
-	}
-	// If not, and we are on windows, try the registry.
-	if(command == null && Utility.isWindows()) {
-	    command = WinRegistry.openCommand(file.getAbsolutePath());
-	}
-	// Failing all that prompt the user to select the program to open this with.
-	if(command == null) {
-	    FileAssociationDialog dialog = new FileAssociationDialog(this);
-	    command = dialog.display(extension);
-	    dialog.destroy();
-	    dialog = null;
-	    if(command != null) {
-		put(extension, command);
-		command = command + " " + file.getAbsolutePath();
+	// If a associations.xml is available in the GLI install directory load it.
+	data_file = new File(Utility.BASE_DIR + DATA_FILENAME);
+	if(data_file.exists()) {
+	    document = Utility.parse(data_file, true);
+	}
+	// Load the default associations xml data file. This can be done using the classloader.
+	else {
+	    document = Utility.parse(Utility.XML_DIRECTORY + DATA_FILENAME, true);
+	}
+	// Initialize the associations. This involves looking through all current associations searching for those with a command of "".
+	if(document != null) {
+	    NodeList entries = (document.getDocumentElement()).getElementsByTagName(ENTRY_ELEMENT);
+	    for(int i = 0; i < entries.getLength(); i++) {
+		Element entry = (Element) entries.item(i);
+		String extension = entry.getAttribute(EXTENSION_ATTRIBUTE);
+		String command = MSMUtils.getValue(entry);
+		// If we encounter a command of "", and we are on windows, we try to automatically set this command.
+		if(command.length() == 0 && Utility.isWindows()) {
+		    // Create a dummy filename with the appropriate extension
+		    String dummy_file = "dummy." + extension;
+		    command = WinRegistry.openCommand(dummy_file);
+		    // If this succeeded add the association.
+		    if(command != null) {
+			// Remember to replace the dummy filename with %1
+			command = command.replaceAll(dummy_file, FILENAME_ARG);
+			// Replace the text in this node.
+			MSMUtils.setValue(entry, command);
+		    }
+		    dummy_file = null;
+		}
+		command = null;
+		extension = null;
+		entry = null;
 	    }
-	}
-	///ystem.err.println("Get command: " + extension + " = " + command);
-	return command;
-    }
-
-    public String getCommandImmediately(String extension) {
-	return (String) get(extension);
-    }
-
+	    entries = null;
+	}
+	else {
+	    Gatherer.println("Didn't parse anything. About to crash.");
+	}
+    }
+    
     public void edit() {
 	FileAssociationDialog dialog = new FileAssociationDialog(this);
 	dialog.display(null);
-	dialog.destroy();
 	dialog = null;		  
     }
 
-    private void load() {
-	// Read in and parse the document.
-	Document document = Utility.parse(ASSOC_FILE, false);
-	if(document != null) {
-				// Recover all the entry nodes of the association root.
-	    for(Node entry_n = document.getDocumentElement().getFirstChild(); entry_n != null; entry_n = entry_n.getNextSibling()) {
-		if(entry_n.getNodeName().equals("Entry")) {
-		    Element entry_e = (Element) entry_n;
-		    String extension = entry_e.getAttribute("extension");
-		    String command = MSMUtils.getValue(entry_e);
-		    put(extension, command);
-		    extension = null;
-		    command = null;
-		    entry_e = null;
+    public String getCommand(File file) {
+	String command = null;
+	if(file.isFile()) {
+	    // Determine extension
+	    String filename = file.getAbsolutePath();
+	    String extension = filename.substring(filename.lastIndexOf(".") + 1);
+	    // Try to retrieve a value from cache
+	    Element entry = getCommand(extension);
+	    if(entry != null) {
+		command = MSMUtils.getValue(entry); 
+		if(command == null || command.length() == 0) {
+		    // If command is null, and we are on windows try searching the registry.
+		    if(Utility.isWindows()) {
+			command = WinRegistry.openCommand(filename);
+		    }
+		    // Otherwise display the dialog and ask the user to enter launching command.
+		    if(command == null || command.length() == 0) {
+			// Show the dialog which forces a user to select the launch command for a certain file.
+			FileAssociationDialog dialog = new FileAssociationDialog(this);
+			command = dialog.display(extension);
+			dialog = null;
+		    }
+		    // Hopefully by now we have a command, or else we're never going to get one. Add the association.
+		    if(command != null) {
+			// If no previous entry existed create one.
+			if(entry == null) {
+			    entry = document.createElement(ENTRY_ELEMENT);
+			    entry.setAttribute(EXTENSION_ATTRIBUTE, extension);
+			    document.getDocumentElement().appendChild(entry);
+			}
+			// Replace the text in this node. Remember to replace the dummy filename with %1
+			MSMUtils.setValue(entry, command.replaceAll(filename, FILENAME_ARG));
+		    }
+		}
+		if(command != null) {
+		    // We have to fix filename under windows to escape the backslashes.
+		    filename = filename.replaceAll(ESCAPE, ESCAPED_ESCAPE);
+		    // Replace %1 with the appropriate filename
+		    command = command.replaceAll(FILENAME_ARG, filename);
 		}
 	    }
-	}
-    }
-
-    private void save() {
-	// Only save if there is something to save.
-	if(size() > 0) {
-	    StringBuffer assoc = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
-	    assoc.append("<!DOCTYPE Associations [\n");
-	    assoc.append("  <!ELEMENT Associations (Entry*)>\n");
-	    assoc.append("  <!ELEMENT Entry (#PCDATA)>\n");
-	    assoc.append("  <!ATTLIST Entry\n");
-	    assoc.append("            extension CDATA #REQUIRED>\n");
-	    assoc.append("]>\n");
-	    assoc.append("\n");
-	    assoc.append("<Associations>\n");
-	    for(Iterator keys = keySet().iterator(); keys.hasNext(); ) {
-		String extension = (String) keys.next();
-		String command = (String) get(extension);
-		assoc.append("\t<Entry extension=\"");
-		assoc.append(extension);
-		assoc.append("\">");
-		assoc.append(command);
-		assoc.append("</Entry>\n");
+	    entry = null;
+	    extension = null;
+	    filename = null;
+	}
+	return command;
+    }
+
+    public Element getCommand(String target_extension) {
+	NodeList entries = document.getDocumentElement().getElementsByTagName(ENTRY_ELEMENT);
+	for(int i = 0; i < entries.getLength(); i++) { 
+	    Element entry = (Element) entries.item(i);
+	    String extension = entry.getAttribute(EXTENSION_ATTRIBUTE);
+	    if(extension.equalsIgnoreCase(target_extension)) {
+		entries = null;
+		extension = null;
+		return entry;
 	    }
-	    assoc.append("</Associations>");
-
-	    String assoc_str = assoc.toString();
-	    assoc = null;
-	    try {
-		FileOutputStream fos = new FileOutputStream(ASSOC_FILE);
-		OutputStreamWriter osw = new OutputStreamWriter(fos, Charset.forName("UTF-8"));
-		int position = 0;
-		int BLOCK_SIZE = 1024;
-		// Write x block sized chunks to file.
-		while(position + BLOCK_SIZE < assoc_str.length()) {
-		    osw.write(assoc_str, position, BLOCK_SIZE);
-		    position = position + BLOCK_SIZE;
-		}
-		// Write the remainder of the buffer.
-		if(position < assoc_str.length()) {
-		    osw.write(assoc_str, position, assoc_str.length() - position);
-		}
-		osw.flush();
-		osw.close();
-		osw = null;
-		fos.close();
-		fos = null;
-		assoc_str = null;
-	    }
-	    catch (Exception error) {
-		Gatherer.printStackTrace(error);
-	    }
-	}
+	}
+	entries = null;
+	return null;
+    }
+
+    public String getCommandString(String target_extension) {
+	Element entry = getCommand(target_extension);
+	if(entry != null) {
+	    return MSMUtils.getValue(entry);
+	}
+	else {
+	    return "";
+	}
+    }
+
+    public String getExtension(int index) {
+	NodeList entries = document.getDocumentElement().getElementsByTagName(ENTRY_ELEMENT);
+	if(0 <= index && index < entries.getLength()) {
+	    Element entry = (Element) entries.item(index);
+	    return entry.getAttribute(EXTENSION_ATTRIBUTE);
+	}
+	return "";
+    }
+
+    public void save() {
+	Utility.export(document, data_file);
     }
 
     public void setCommand(String extension, String command) {
-	///ystem.err.println("Set command: " + extension + " = " + command);
-	if(command == null || command.length() == 0) {
-	    remove(extension);
-	}
-	else {
-	    put(extension, command);
-	}
+	Gatherer.println("Set Launch: " + extension + " with " + command);
+	// Retrieve any existing entry for this extension
+	Element entry = getCommand(extension);
+	// If no previous entry existed create one.
+	if(entry == null) {
+	    entry = document.createElement(ENTRY_ELEMENT);
+	    entry.setAttribute(EXTENSION_ATTRIBUTE, extension);
+	    document.getDocumentElement().appendChild(entry);
+	}
+	// Replace the text in this node. If the user has used filename instead of %1 then too bad.
+	MSMUtils.setValue(entry, command);
+	entry = null;
+    }
+
+    public int size() {
+	NodeList entries = document.getDocumentElement().getElementsByTagName(ENTRY_ELEMENT);
+	return entries.getLength();
     }
 }
-
-
-
