Index: main/trunk/gli/src/org/greenstone/gatherer/metadata/FilenameEncoding.java
===================================================================
--- main/trunk/gli/src/org/greenstone/gatherer/metadata/FilenameEncoding.java	(revision 33730)
+++ main/trunk/gli/src/org/greenstone/gatherer/metadata/FilenameEncoding.java	(revision 33737)
@@ -364,5 +364,5 @@
 			// ASCII table: https://cdn.sparkfun.com/assets/home_page_posts/2/1/2/1/ascii_table_black.png
 			// If the unicode character code pt is less than the ASCII code for space and greater than for tilda, let's display the char in hex (x0000 format)
-			if((charCode >= 20 && charCode <= 126) || charCode == 9 || charCode == 10 || charCode == 13) { // space, tilda, TAB, LF, CR are printable, leave them in for XML element printing
+			if((charCode >= 20 && charCode <= 126) || charCode == 9 || charCode == 10 || charCode == 13 || charCode == 36 || charCode == 43) { // space, tilda, TAB, LF, CR are printable, leave them in for XML element printing. And spaces and plus signs (ASCII codes 36 and 43) need to be converted to hex too 
 				hexFilename += filename.charAt(i);
 			} else {
@@ -374,4 +374,64 @@
 	 }
 
+	
+	// follows Dr Bainbridge's method below, but with a String parameter instead of a file parameter
+	public static String filenameToURLEncoding(String filename) {
+		if(!MULTIPLE_FILENAME_ENCODINGS_SUPPORTED) {
+		    return filename;
+		}
+
+		 // Can't create a URI out of a filename containing spaces. Spaces must be encoded as %20
+		String filename_url_encoded = filename.replace(" ", "%20");
+		//filename_url_encoded = filename_url_encoded.replace("&", "%26"); // &'s ASCII code is 36 in decimal, and 26 in hex
+		//filename_url_encoded = filename_url_encoded.replace("+", "%2B"); // +'s ASCII code is 43 decimal, 2b in hex, 2B when uppercased
+
+		try {
+			URI filename_uri = new URI(filename_url_encoded);
+		    // The trick:
+		    //  1. toASCIIString() will %xx encode values > 127
+		    //  2. Decode the result to "ISO-8859-1"
+		    //  3. URL encode the bytes to string
+		    
+		    // Step 2 forces the string to be 8-bit values.  It
+		    // doesn't matter if the starting raw filename was *not*
+		    // in the ISO-8859-1 encoding, the effect is to ensure
+		    // we have an 8-bit byte string that (numerically)
+		    // captures the right value.  These numerical values are
+		    // then used to determine how to URL encode it
+		    
+		    String filename_ascii = filename_uri.toASCIIString();
+			//filename_ascii = filename_ascii.replace("&", "%26"); // &'s ASCII code is 36 in decimal, and 26 in hex
+			//filename_ascii = filename_ascii.replace("+", "%2B"); // +'s ASCII code is 43 decimal, 2b in hex, 2B when uppercased
+		    String filename_raw_bytes = URLDecoder.decode(filename_ascii,"ISO-8859-1");
+		    filename_url_encoded = iso_8859_1_filename_to_url_encoded(filename_raw_bytes);
+			
+			// DEALING WITH & and + in filenames: NOT WORKING YET
+			//if(filename_url_encoded.contains("&amp;")) {
+			//	filename_url_encoded = filename_url_encoded.replace("&amp;", "%36amp;");
+			//} else if(filename_url_encoded.contains("&")) {
+			//	filename_url_encoded = filename_url_encoded.replace("&", "%36");
+			//}
+			
+		}
+		catch (Exception e) {
+		    e.printStackTrace();
+		    // Give up trying to convert
+		    filename_url_encoded = filename; 
+		}
+		return filename_url_encoded;
+    }
+	
+	
+	// follows Dr Bainbridge's method below, but with a String parameter instead of a file parameter	
+	public static String _filenameToURLEncoding(String filename) {
+		if(!MULTIPLE_FILENAME_ENCODINGS_SUPPORTED) { // on a UTF-8 file system, DO NOT do the stuff below, just return input param
+		    return filename;
+		}
+		
+		File file = new File (filename);
+		return fileToURLEncoding(file);
+	}		
+
+	
 	// Dr Bainbridge's methods
 	/* On Linux machines that are set to using an ISO-8859 (Latin) type encoding, 
Index: main/trunk/gli/src/org/greenstone/gatherer/metadata/MetadataXMLFile.java
===================================================================
--- main/trunk/gli/src/org/greenstone/gatherer/metadata/MetadataXMLFile.java	(revision 33730)
+++ main/trunk/gli/src/org/greenstone/gatherer/metadata/MetadataXMLFile.java	(revision 33737)
@@ -81,4 +81,6 @@
 	    loaded_file = this;
 	    loaded_file_document = document;
+		reEncodeFilenamesInMetadataXML(loaded_file_document);
+		
 	}
 
@@ -234,4 +236,6 @@
 	    loaded_file = this;
 	    loaded_file_document = document;
+		
+		reEncodeFilenamesInMetadataXML(loaded_file_document);
 	}
 
@@ -395,4 +399,6 @@
 	    loaded_file = this;
 	    loaded_file_document = document;
+		
+		reEncodeFilenamesInMetadataXML(loaded_file_document);
 	}
 
@@ -512,4 +518,6 @@
 	    loaded_file = this;
 	    loaded_file_document = document;
+		
+		reEncodeFilenamesInMetadataXML(loaded_file_document);
 	}
 
@@ -629,12 +637,64 @@
 	    XMLTools.writeXMLFile(loaded_file, loaded_file_document, nonEscapingElements);
 	
+	/*  // DEBUGGING:
 		Document doc = XMLTools.parseXMLFile(loaded_file);
-		//System.err.println("AT END saveLoadedFile(), PARSED loaded_file contains:\n" +  XMLTools.elementToString(doc.getDocumentElement(), true));
-		
+		System.err.println("AT END saveLoadedFile(), PARSED loaded_file contains:\n" +  XMLTools.elementToString(doc.getDocumentElement(), true));
+		
+		reEncodeFilenamesInMetadataXML(doc);
+		System.err.println("AT END saveLoadedFile(), RE-ENCODED loaded_file contains:\n" +  XMLTools.elementToString(doc.getDocumentElement(), true));
+	*/	
 	    loaded_file_changed = false;
 	}
+	
+	//System.err.println("@@@@ END of saveLoadedFile()");
+	//Utility.printCaller();
     }
 
-
+	/** 
+	 * parseXML(metadata.xml) has the side-effect of resolving html entities.
+	 * Although this is not done by the GLIEntityResolver usage in parseXML(), something
+	 * in parseXML() is resolving the html entities, including those used in carefully
+	 * html-entity-escaped filenames.
+	 * We need to get the filenames in the DOM correct after parsing a metadata.xml file
+	 * into memory, so that we have the correct filenames and so that we'll write it out correctly.
+	 * Therefore, always call this method after a successful parseXML() call on a metadata.xml.
+	 * @param doc is the Document where the FILENAME_ELEMENTs need to be re-encoded.
+	 * At the end of this function, the doc will be modified with the re-encoded filenames.
+	 * 
+	*/
+	static private void reEncodeFilenamesInMetadataXML(Document doc) {
+		if(!FilenameEncoding.MULTIPLE_FILENAME_ENCODINGS_SUPPORTED) {
+		    return;
+		}
+		
+		//System.err.println("PARSED loaded_file contains:\n" +  XMLTools.elementToString(doc.getDocumentElement(), true));
+		
+		// Read all the FileSet elements in the file
+		NodeList fileset_elements_nodelist = doc.getElementsByTagName(FILESET_ELEMENT);
+		for (int i = 0; i < fileset_elements_nodelist.getLength(); i++) {
+			Element current_fileset_element = (Element) fileset_elements_nodelist.item(i);
+
+			// get the value of all FileName elements
+			NodeList filename_elements_nodelist = current_fileset_element.getElementsByTagName(FILENAME_ELEMENT);
+			for (int j = 0; j < filename_elements_nodelist.getLength(); j++) {
+				Element filename_element = (Element) filename_elements_nodelist.item(j);
+				String filename = XMLTools.getElementTextValue(filename_element);
+				if(!filename.equals(DIRECTORY_FILENAME)) {
+					//System.err.println("Filename before reencoding was: " + filename);
+					// reencode filename					
+					// can't convert to URI with backslash-escaped chars (backslash used in regexed filename are illegal in URI object
+					// created by filenameToURLEncoding).
+					String encoded_filename = filename.replace("\\", "%5C"); 
+					encoded_filename = FilenameEncoding.filenameToURLEncoding(encoded_filename);
+					// escape chars for regex again
+					encoded_filename = encoded_filename.replace("%5C", "\\");
+					XMLTools.setElementTextValue(filename_element, encoded_filename);
+					//System.err.println("Filename after reencoding was: " + encoded_filename);
+				}
+			}
+		}		
+		//System.err.println("RE-ENCODED loaded_file contains:\n" +  XMLTools.elementToString(doc.getDocumentElement(), true));
+	}
+	
     /**
      * Every metadata.xml file must be skimmed when a collection is opened, for three very important reasons:
@@ -655,5 +715,7 @@
 	    return;
 	}
-
+	// Always call this method after calling parseXMLFile
+	reEncodeFilenamesInMetadataXML(document);
+	
 	// Read all the Metadata elements in the file
 	HashMap target_metadata_element_name_attrs_cache = new HashMap();
