Changeset 23433

Show
Ignore:
Timestamp:
09.12.2010 22:27:33 (9 years ago)
Author:
ak19
Message:

GLI now has a gs.FilenameEncoding? metadata field which appears like all the others in GLI's EnrichPane?, but is unique in that this metadata (once set, changed or removed) must be applied to the affected filenames in the Collection Tree. More importantly, the changes made for this are to allow GLI's java code to interact with the recent changes to Perl where strings were made unicode-aware (for proper regex matching) but which required other changes elsewhere. To still support filenames with different encodings Perl used URL encoded versions of filenames representing characters' code point values in URL encoding. This required that GLI write out URL encoded filenames to the metadata.xml files that are associated with each folder level of a collection, so that Perl can read them. In this way, they can both speak of the same filenames. Only works on unicode 16 (such as latin-1), non-UTF8 systems. The latter is a requirement since Java uses the filesystem encoding from startup. If it is UTF8, non-recognised characters are replaced by the invalid char for UTF8. This process being destructive, we can't get the original filenames' bytecodes back. The changes made to GLI will work on Windows which is UTF-16 (windows codepage 1252), presumably also Macs (some kind of UTF-16) and also works on Native Latin 1 Linux systems. UTF-8 Linux systems need to be reconfigured to Native Latin-1, or if not installed, an administrator can install it easily.

Location:
main/trunk/gli
Files:
1 added
12 modified

Legend:

Unmodified
Added
Removed
  • main/trunk/gli/classes/dictionary.properties

    r23143 r23433  
    652652General.Warning:Warning 
    653653General.Yes:Yes 
     654General.MultipleFileNamesNotSupported.Message:Your locale seems to be in UTF-8. Non-UTF-8 filenames are not supported by your setup. 
     655General.MultipleFileNamesNotSupported.Title:Filename Encoding Support 
    654656#**************************** 
    655657# 
  • main/trunk/gli/metadata/greenstone.mds

    r23393 r23433  
    4343    <Language code="en"> 
    4444      <Attribute name="label">Filename Encoding</Attribute> 
    45       <Attribute name="definition">TODO</Attribute> 
     45      <Attribute name="definition">The encoding of the filename. If this is known, it can be manually set here.</Attribute> 
     46      <Attribute name="comment">If not manually specified, Greenstone will try to guess the encoding of the filename upon building, which may or may not be correct.</Attribute> 
    4647    </Language> 
    4748  </Element> 
  • main/trunk/gli/src/org/greenstone/gatherer/Gatherer.java

    r23143 r23433  
    5656import org.greenstone.gatherer.gui.WarningDialog; 
    5757import org.greenstone.gatherer.gui.FedoraLogin; 
     58import org.greenstone.gatherer.metadata.FilenameEncoding; 
    5859import org.greenstone.gatherer.remote.RemoteGreenstoneServer; 
    5960import org.greenstone.gatherer.util.JarTools; 
     
    471472        } 
    472473 
     474        // Check that the local can support multiple filename encodings 
     475            //System.err.println("#### Java identifies current Locale as (file.encoding): " 
     476            //  + System.getProperty("file.encoding")); 
     477        if(System.getProperty("file.encoding").equals("UTF-8")){ 
     478            // If the locale is UTF-8, Java will interpret all filename bytes as UTF-8,  
     479            // which is a destructive process as it will convert characters not recognised 
     480            // by UTF-8 into the invalid character, rather than preserving the bytecodes. 
     481            // This has the effect that non-UTF8 encoded filenames on a system set to a  
     482            // UTF-8 locale are not 'seen' by Java (if they contain non-ASCII characters). 
     483            multipleFilenameEncodingsNotSupported();         
     484            FilenameEncoding.MULTIPLE_FILENAME_ENCODINGS_SUPPORTED = false;  
     485            FilenameEncoding.URL_FILE_SEPARATOR = File.separator; 
     486        } else { 
     487            FilenameEncoding.MULTIPLE_FILENAME_ENCODINGS_SUPPORTED = true; 
     488            FilenameEncoding.URL_FILE_SEPARATOR = "/"; // URL file separator is always "/"  
     489        } 
     490         
    473491        // Set the default font for all Swing components. 
    474492        FontUIResource default_font = Configuration.getFont("general.font", true); 
     
    852870                + File.separator + "cgi" + File.separator + "gsdl3site.cfg"; 
    853871        } else { // cgi-bin/gsdlsite.cfg 
    854             return Configuration.gsdl_path + File.separator  
     872            return Configuration.gsdl_path /* + File.separator */ 
    855873                + "cgi-bin" + File.separator + "gsdlsite.cfg"; 
    856874        } 
     
    12581276    } 
    12591277 
     1278    /** Prints a warning message about the OS not supporting multiple filename encodings.  */ 
     1279    static private void multipleFilenameEncodingsNotSupported() {    
     1280        WarningDialog dialog = new WarningDialog("warning.NoEncodingSupport",  
     1281            Dictionary.get("General.MultipleFileNamesNotSupported.Title"),  
     1282            Dictionary.get("General.MultipleFileNamesNotSupported.Message"), null, false); 
     1283        dialog.display(); 
     1284        dialog.dispose(); 
     1285        dialog = null; 
     1286    } 
    12601287 
    12611288    /** Sets up the proxy connection by setting JVM Environment flags and creating a new Authenticator. 
  • main/trunk/gli/src/org/greenstone/gatherer/collection/CollectionManager.java

    r23143 r23433  
    6262import org.greenstone.gatherer.gui.WarningDialog; 
    6363import org.greenstone.gatherer.metadata.DocXMLFileManager; 
     64import org.greenstone.gatherer.metadata.FilenameEncoding; 
    6465import org.greenstone.gatherer.metadata.MetadataChangedListener; 
    6566import org.greenstone.gatherer.metadata.MetadataSet; 
     
    15521553    if (collection != null) { 
    15531554        collection.setMetadataChanged(true); 
     1555         
     1556        // we're only going to refresh the tree's already visible nodes (and reselect them) IFF  
     1557        // gs.FilenameEncoding meta was set on any files/folders and the filenames of the 
     1558        // affected CollectionTreeNodes need to be recalculated 
     1559        if(FilenameEncoding.isRefreshRequired()) { 
     1560             
     1561            // refreshes the relevant part of the tree 
     1562            TreePath[] paths = collection_tree.getSelectionPaths();         
     1563            if(paths != null) { 
     1564                for(int i = 0; i < paths.length; i++) { 
     1565                collection_tree_model.refresh(paths[i]); 
     1566                collection_tree.setSelectionPath(paths[i]); 
     1567                } 
     1568            } 
     1569            // refreshed the tree, so turn off the requirement to refresh 
     1570            FilenameEncoding.setRefreshRequired(false);      
     1571        } 
    15541572    } 
    15551573    } 
  • main/trunk/gli/src/org/greenstone/gatherer/collection/CollectionTreeNode.java

    r16838 r23433  
    3232import org.greenstone.gatherer.cdm.CollectionDesignManager; 
    3333import org.greenstone.gatherer.file.FileNode; 
     34import org.greenstone.gatherer.metadata.FilenameEncoding; 
    3435import org.greenstone.gatherer.util.JarTools; 
    3536import java.util.Set; 
    3637import java.util.Iterator; 
    37 import java.nio.charset.Charset; 
    3838 
    3939 
     
    5454    { 
    5555    super(file); 
     56        // the super call will additionally call calcDisplayString() which will get any 
     57        // applicable the filename encoding and apply it to the file's name for display. 
    5658 
    5759    this.is_explodable = CollectionDesignManager.plugin_manager.isFileExplodable(file); 
     
    8183    return is_srcreplaceable; 
    8284    } 
     85 
     86    /** This method returns a string representation of the filenodes in the  
     87    * <i>Collection</i> Tree, that can then be displayed in the tree. If the  
     88    * filename encoding is specified as metadata with the file, then this 
     89    * method will apply that to the filename's bytes to get the displayName. 
     90    */ 
     91    protected String calcDisplayString() {       
     92        // metadata.xml files in collections don't get displayed anyway 
     93        if(file.getName().equals("metadata.xml")) {  
     94            return super.calcDisplayString(); 
     95        } 
     96         
     97        if(!FilenameEncoding.MULTIPLE_FILENAME_ENCODINGS_SUPPORTED) { 
     98            return super.calcDisplayString(); 
     99        } 
     100 
     101        // Else: try to encode the display string from the  
     102        // file_to_encoding map. If that fails, just use the locale  
     103        String displayName = null; 
     104        String urlEncodedPath = getURLEncodedFilePath(); 
     105        String encoding = FilenameEncoding.findFilenameEncoding(file, urlEncodedPath, false); 
     106 
     107        // if it isn't the same as the current encoding already applied, reapply 
     108        // it which will set the filenameEncoding again as well 
     109        if(!encoding.equals(getFilenameEncoding())) { 
     110            displayName = reencodeDisplayName(encoding); // may return null 
     111        } 
     112         
     113        if(displayName == null) { 
     114            if(FilenameEncoding.DEBUGGING) { 
     115                displayName = getURLEncodedFileName();  
     116            } else { 
     117                displayName = super.calcDisplayString(); 
     118            } 
     119        } 
     120         
     121        return displayName; 
     122    } 
     123 
     124    /** Call this if the filename encoding has changed and needs to be  
     125    * recalculated and re-applied to the filename. This is only for display, 
     126    * so it only gets applied to the urlEncodedFileName (not urlEncodedFilePath).  
     127    * Note that the filenameEncoding may not be the same as the given encoding 
     128    * at the end of this method (in case it was a charset alias of the encoding).  
     129    * This method both sets and returns the displayFileName member variable.  
     130    * @return the reencoded display name, if the encoding was a recognised alias 
     131    * in which case the filenameEncoding will store the canonical name.  */ 
     132    public String reencodeDisplayName(String encoding) { 
     133        if(!FilenameEncoding.MULTIPLE_FILENAME_ENCODINGS_SUPPORTED) { 
     134            return displayFileName; // may still be null 
     135        } 
     136 
     137        filenameEncoding = encoding; // clear previous value 
     138 
     139        if(filenameEncoding.equals("")) { 
     140            displayFileName = super.calcDisplayString(); // *FileNode* calcDisplayString 
     141            return displayFileName; 
     142        } 
     143 
     144        try{ 
     145            displayFileName = new String(file.getName().getBytes(), filenameEncoding); 
     146        } catch(Exception e) {  
     147            // IllegalCharsetName-, UnsupportedCharset- or UnsupportedEncoding-Exception 
     148            // Store the unsupported encoding, but display with filesystem (or URL) encoding 
     149            filenameEncoding = encoding; 
     150             
     151            if(FilenameEncoding.DEBUGGING) { 
     152                displayFileName = getURLEncodedFileName(); 
     153            } else { 
     154                displayFileName = super.calcDisplayString();  
     155            } 
     156        }  
     157        return displayFileName; 
     158    } 
     159         
     160    /** Can call this upon refreshing a FileNode (this CollectionTreeNode). It makes 
     161    * sure the display names of the visible nodes in the CollectionTree aren't stale */ 
     162    public void refreshDescendantEncodings() { 
     163        // now recalculate encodings for all visible children 
     164         
     165        // Don't bother with the encoding stuff when multiple filename encodings  
     166        // are not supported because the system is UTF-8 (not Native-Latin-1) and  
     167        // Java interprets all filename bytes as UTF-8, which destructively  
     168        // converts unrecognised characters (for UTF-8) into the invalid character.  
     169 
     170        if(!FilenameEncoding.MULTIPLE_FILENAME_ENCODINGS_SUPPORTED  
     171                || child_nodes == null)  
     172        { 
     173            return; 
     174        }        
     175         
     176        // get and apply the filename encoding, if any 
     177        for(int i = 0; i < child_nodes.size(); i++) { 
     178            CollectionTreeNode child_node = (CollectionTreeNode) child_nodes.get(i);             
     179            String urlEncodedPath = child_node.getURLEncodedFilePath();      
     180             
     181            String encoding = FilenameEncoding.findFilenameEncoding( 
     182                    child_node.getFile(), urlEncodedPath, false); 
     183             
     184            // if current encoding is different from the existing one, re-apply encoding 
     185            if(!child_node.getFilenameEncoding().equals(encoding)) { 
     186            child_node.reencodeDisplayName(encoding); 
     187            } 
     188        } 
     189    } 
     190     
     191    // Unused at present 
     192    /** Call when the filename encoding of this folder level fileNode has changed 
     193    * and therefore the filename encodings of the descendant fileNodes have to be 
     194    * reset (their entries in the file_to_encoding hashmap cleared), so that we know 
     195    * to recalculate these later.  Call when deleting, moving or renaming col nodes */ 
     196    public void resetDescendantEncodings() { 
     197        if(FilenameEncoding.MULTIPLE_FILENAME_ENCODINGS_SUPPORTED) { 
     198            resetDescendantEncodings(this.file, this.getURLEncodedFilePath()); 
     199             
     200            // for an actual file/folder delete, want to remove all corresponding  
     201            // entries from the hashtable without recalculating any encodings  
     202            // (files have been deleted, so no there will be no filenames to display). 
     203            // So don't do this here: refreshDescendantEncodings(); 
     204            // It's done near the end of FileNode.map(). 
     205        } 
     206    } 
     207     
     208    // Together with the above, unused at present 
     209    private static void resetDescendantEncodings(File f, String urlEncodedPath)  
     210    { 
     211        // remove this file f's urlencoded path name from the file_to_encoding Map 
     212        FilenameEncoding.map.remove(urlEncodedPath); 
     213             
     214        if(f.isDirectory()) { 
     215            File[] children = f.listFiles();         
     216            for(int i = 0; i < children.length; i++) { 
     217                urlEncodedPath = FilenameEncoding.fileToURLEncoding(children[i]); 
     218                resetDescendantEncodings(children[i], urlEncodedPath);  
     219                        // sets filenameEncoding var 
     220            } 
     221        } 
     222    } 
     223 
    83224} 
  • main/trunk/gli/src/org/greenstone/gatherer/file/FileNode.java

    r16984 r23433  
    88import javax.swing.tree.*; 
    99import org.greenstone.gatherer.DebugStream; 
     10import org.greenstone.gatherer.metadata.FilenameEncoding; 
    1011import org.greenstone.gatherer.util.ArrayTools; 
    1112 
     
    1920    protected File file = null; 
    2021    protected FileSystemModel model = null; 
    21     protected MutableTreeNode parent = null; 
     22    protected MutableTreeNode parent = null;     
     23 
     24    protected String urlEncodedFileName = ""; 
     25    protected String urlEncodedFilePath = ""; 
     26    protected String filenameEncoding = ""; 
    2227    /** The string that is displayed as the filename. Attempts to be in the correct encoding. */ 
    2328    protected String displayFileName = null; 
    24  
     29     
    2530 
    2631    public FileNode(File file) 
    2732    { 
    28     this.file = file; 
    29  
    30     // Files cannot have children 
    31     if (file != null && !file.isDirectory()) { //file.isFile()) { 
    32         // Cache this result to prevent unceasing missing disk messages being thrown if the 
    33         // removable media was, um, removed after directory mapped 
    34         this.allows_children = false; 
    35         displayFileName = calcDisplayString(); 
    36     }    
    37     } 
    38  
    39  
    40      /** This method returns a string representation of the filenodes in the Collection 
    41      * Tree, that can then be displayed in the tree.  
    42      * We'll initially assume that the filenames are utf8 encoded and so convert the 
    43      * filename into utf8 for proper presentation in the Collection tree pane. 
    44      * If the filenames are not utf8, then the conversion would have introduced funny 
    45      * characters. Therefore, when converting to utf8, if the converted filename  
    46      * contains the special character '\ufffd', then we know the conversion did not work 
    47      * and we return the original string which may or may not be properly presented by 
    48      * default. 
    49      * See http://java.sun.com/j2se/1.4.2/docs/api/java/nio/charset/CharsetDecoder.html 
    50      * which says "How a decoding error is handled depends upon the action requested for  
    51      * that type of error, which is described by an instance of the CodingErrorAction class. 
    52      * The possible error actions are to ignore the erroneous input, report the error to 
    53      * the invoker via the returned CoderResult object, or replace the erroneous input with 
    54      * the current value of the replacement string. The replacement has the initial value  
    55      * "\uFFFD"; its value may be changed via the replaceWith method." 
    56      * The following made me think that String(byte[], String charsetName) constructor may 
    57      * use the replacement value \uFFFD. 
    58      * http://www.experts-exchange.com/Programming/Programming_Languages/Java/Q_20512969.html 
    59      * mentions the following which made me think of this:  
    60      * convertedStr = convertedStr.replace('\ufffd', ' '); 
    61      */ 
    62     protected String calcDisplayString() { 
    63     String filename = file.getName(); 
    64     try{ 
    65         String utf8filename = new String(filename.getBytes(), "UTF8"); 
    66         if(utf8filename.indexOf('\ufffd') == -1) { 
    67         return utf8filename; 
    68         } else { // contains the character indicating that it's invalid utf8 
    69         // return the original string 
    70         return filename; 
    71         } 
    72     } catch(java.io.UnsupportedEncodingException e) { 
    73         return filename; 
    74     } 
     33        this.file = file; 
     34 
     35        if (file != null) { 
     36            // Files cannot have children 
     37            if(file.isFile()) { 
     38                // Cache this result to prevent unceasing missing disk messages being thrown if the 
     39                // removable media was, um, removed after directory mapped 
     40                this.allows_children = false; 
     41            } 
     42            filenameEncoding = ""; 
     43            urlEncodedFilePath = FilenameEncoding.calcURLEncodedFilePath(file); 
     44            urlEncodedFileName = FilenameEncoding.calcURLEncodedFileName(urlEncodedFilePath); 
     45 
     46            // work out the display string (extra special processing for CollectionTreeNodes) 
     47            displayFileName = calcDisplayString();       
     48        } 
     49    } 
     50 
     51    public String getURLEncodedFileName() { return urlEncodedFileName; } 
     52 
     53    public String getURLEncodedFilePath() { return urlEncodedFilePath; } 
     54 
     55    public String getFilenameEncoding() { return filenameEncoding; } 
     56 
     57 
     58    /** This method returns a string representation of the filenodes in the tree,  
     59    * that can then be displayed in the tree. Overridden in subclass CollectionTreeNode. 
     60    * Turn FilenameEncoding.DEBUGGING on to see URLEncoded filenames. 
     61    */ 
     62    protected String calcDisplayString() {   
     63        if(FilenameEncoding.DEBUGGING) { 
     64            return getURLEncodedFileName(); 
     65        } else { 
     66            return file.getName(); 
     67        } 
    7568    } 
    7669 
     
    314307            } 
    315308        } 
     309         
     310            // in case any filename encodings had gone stale,  
     311            // (recalculate these and) refresh the display name 
     312            refreshDescendantEncodings();            
     313                     
    316314        } 
    317315 
     
    327325    } 
    328326 
    329  
     327    // overridden in subclass CollectionTreeNode to reset and reencode display strings 
     328    public void resetDescendantEncodings() {} 
     329    public void refreshDescendantEncodings() {} 
     330     
     331     
    330332    public void setModel(FileSystemModel model) { 
    331333    this.model = model; 
  • main/trunk/gli/src/org/greenstone/gatherer/gui/EnrichPane.java

    r18593 r23433  
    4444import org.greenstone.gatherer.collection.CollectionTreeNode; 
    4545import org.greenstone.gatherer.gui.tree.DragTree; 
     46import org.greenstone.gatherer.metadata.FilenameEncoding; 
    4647import org.greenstone.gatherer.metadata.MetadataElement; 
    4748import org.greenstone.gatherer.metadata.MetadataValue; 
     
    287288  public void valueChanged(TreeSelectionEvent event) 
    288289    { 
     290    if(FilenameEncoding.isRefreshRequired()) {  
     291        // The CollectionTree is in the process of being re-built to deal with filename-encodings,  
     292        // so don't mess up the table during that time. 
     293        // (CollectionTreeNode.refreshDescendantEncodings() doesn't cope well if this method 
     294        // responds on Tree selection changes while the tree is updating its encodings, resulting in lost 
     295        // and misplaced metadata values in the table and thereby in the metadata.xml files themselves). 
     296        return; 
     297    } 
     298     
     299     
    289300    // If we haven't got focus then it must have been a selection in the Gather pane, so don't bother rebuilding 
    290301    if (has_focus == false) { 
  • main/trunk/gli/src/org/greenstone/gatherer/gui/GUIManager.java

    r23143 r23433  
    5959import org.greenstone.gatherer.gui.metaaudit.MetaAuditFrame; 
    6060import org.greenstone.gatherer.gui.tree.DragTree; 
     61import org.greenstone.gatherer.metadata.FilenameEncoding; 
    6162import org.greenstone.gatherer.metadata.MetadataSet; 
    6263import org.greenstone.gatherer.metadata.MetadataXMLFileManager; 
     
    295296    tab_pane.setSelectedComponent(gather_pane); 
    296297    Gatherer.c_man.closeCollection(); 
     298    FilenameEncoding.closeCollection(); // clear filename-to-encodings map 
    297299    } 
    298300 
  • main/trunk/gli/src/org/greenstone/gatherer/metadata/MetadataValueTableModel.java

    r13398 r23433  
    139139 
    140140    MetadataValueTableEntry metadata_value_table_entry = (MetadataValueTableEntry) metadata_value_table_entries.get(row); 
     141     
     142    if(metadata_value_table_entry == null) { 
     143        System.err.println("\n@@@@@ MetadataValueTableModel.getValueAt(): metadata_value_table_entry is unexpectedly null!\n"); 
     144        return null; 
     145    } 
     146     
    141147    if (col == 0 && metadata_value_table_entry.isInheritedMetadata()) { 
    142148        return metadata_value_table_entry.getFolderMetadataInheritedFrom(); 
     
    213219    { 
    214220    MetadataValueTableEntry metadata_value_table_entry = getMetadataValueTableEntry(row); 
    215  
     221    if(metadata_value_table_entry == null) { 
     222        System.err.println("\n@@@@@ MetadataValueTableModel.setValueAt(): metadata_value_table_entry is unexpectedly null!\n"); 
     223        return; 
     224    } 
     225     
    216226    // If nothing has changed no action is necessary 
    217227    String old_metadata_value = metadata_value_table_entry.getFullValue(); 
     
    252262        MetadataValueTreeNode metadata_value_tree_node = metadata_element.addMetadataValue((String) new_metadata_value); 
    253263        MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node); 
    254         metadata_value.setIsAccumulatingMetadata(true); 
     264        metadata_value.setIsAccumulatingMetadata(true);      
    255265        (new AppendMetadataTask(metadata_value)).run(); 
    256266    } 
  • main/trunk/gli/src/org/greenstone/gatherer/metadata/MetadataXMLFile.java

    r23394 r23433  
    3131import java.util.*; 
    3232import org.greenstone.gatherer.DebugStream; 
     33import org.greenstone.gatherer.collection.CollectionTreeNode; 
    3334import org.greenstone.gatherer.util.XMLTools; 
    3435import org.w3c.dom.*; 
     
    4546    static final private String METADATA_ELEMENT = "Metadata"; 
    4647 
     48    /** Special metadata field: the filename encoding is a unique sort of metadata in 
     49     * that it is not just information stored with a collection file, but also needs to 
     50     * be applied in real-time to the collection file (to its filename) for display. */ 
     51    static final public String FILENAME_ENCODING_METADATA = "gs.filenameEncoding"; 
     52     
    4753    // To speed things up a bit we keep the last accessed metadata.xml file in memory 
    4854    static private File loaded_file = null; 
     
    5763 
    5864 
    59     public void addMetadata(File file, ArrayList metadata_values) 
     65    public void addMetadata(CollectionTreeNode file_node, ArrayList metadata_values) 
    6066    { 
    6167    // If this metadata.xml file isn't the one currently loaded, load it now 
     
    7682 
    7783    // Determine the file's path relative to the location of the metadata.xml file 
    78     String metadata_xml_file_directory_path = getParentFile().getAbsolutePath(); 
    79     String file_relative_path = file.getAbsolutePath().substring(metadata_xml_file_directory_path.length()); 
    80     if (file_relative_path.startsWith(File.separator)) { 
    81         file_relative_path = file_relative_path.substring(File.separator.length()); 
     84    String metadata_xml_file_directory_path = FilenameEncoding.fileToURLEncoding(getParentFile()); 
     85    String file_relative_path = file_node.getURLEncodedFilePath().substring(metadata_xml_file_directory_path.length()); 
     86    if (file_relative_path.startsWith(FilenameEncoding.URL_FILE_SEPARATOR)) { 
     87        file_relative_path = file_relative_path.substring(FilenameEncoding.URL_FILE_SEPARATOR.length()); 
    8288    } 
    8389 
     
    127133        appropriate_fileset_element.appendChild(new_description_element); 
    128134 
     135        // add the fileset element for .* at the top: especially important for 
     136        // non-accumulating (and override mode) meta. Other type fileset elements can be appended 
    129137        if(file_path_regexp.equals(DIRECTORY_FILENAME)) { 
    130           loaded_file_document.getDocumentElement().insertBefore(appropriate_fileset_element, loaded_file_document.getDocumentElement().getFirstChild()); 
     138            loaded_file_document.getDocumentElement().insertBefore(appropriate_fileset_element, 
     139                    loaded_file_document.getDocumentElement().getFirstChild()); 
    131140        } else { 
    132141          loaded_file_document.getDocumentElement().appendChild(appropriate_fileset_element); 
     
    147156        metadata_value_string = metadata_value_string.replaceAll("\\]", "&#093;"); 
    148157 
     158        // the gs.filenameEncoding metadata is unique in that, when added, removed or 
     159        // changed, it must be applied on the file(name) whose metadata has been adjusted 
     160        if(metadata_element_name_full.equals(FILENAME_ENCODING_METADATA)) { 
     161            metadata_value_string = processFilenameEncoding(file_path_regexp,  
     162                                        file_node, metadata_value_string, false); 
     163                              // true only if removing meta 
     164        } 
     165 
    149166        // Check if this piece of metadata has already been assigned to this FileSet element 
    150167        boolean metadata_already_assigned = false; 
     
    156173        String current_metadata_element_name_full = current_metadata_element.getAttribute("name"); 
    157174        if (current_metadata_element_name_full.equals(metadata_element_name_full)) { 
    158           // if the metadata must not accumulate, then edit teh current value 
     175          // if the metadata must not accumulate, then edit the current value 
    159176          if (!metadata_value.isAccumulatingMetadata()) { 
    160177            XMLTools.setNodeText(current_metadata_element, metadata_value_string); 
     
    196213 
    197214 
    198     public ArrayList getMetadataAssignedToFile(File file) 
     215    public ArrayList getMetadataAssignedToFile(File file, boolean fileEncodingOnly) 
    199216    { 
    200217    // If this metadata.xml file isn't the one currently loaded, load it now 
     
    214231    } 
    215232 
    216     // Determine the file's path relative to the location of the metadata.xml file 
     233    // Determine the file's path relative to the location of the metadata.xml file   
     234    String file_relative_path = FilenameEncoding.fileToURLEncoding(file); 
    217235    File metadata_xml_file_directory = getParentFile(); 
    218     String file_relative_path = file.getAbsolutePath().substring(metadata_xml_file_directory.getAbsolutePath().length()); 
    219     if (file_relative_path.startsWith(File.separator)) { 
    220         file_relative_path = file_relative_path.substring(File.separator.length()); 
     236    String metadata_xml_file_directory_path = FilenameEncoding.fileToURLEncoding(metadata_xml_file_directory); 
     237    file_relative_path = file_relative_path.substring(metadata_xml_file_directory_path.length()); 
     238 
     239    if (file_relative_path.startsWith(FilenameEncoding.URL_FILE_SEPARATOR)) { 
     240        file_relative_path = file_relative_path.substring(FilenameEncoding.URL_FILE_SEPARATOR.length()); 
    221241    } 
    222242 
     
    255275 
    256276        // This fileset specifies metadata for the folder the file is in 
    257         if (file_relative_path.startsWith(current_filename_element_value + File.separator)) { 
     277        if (file_relative_path.startsWith(current_filename_element_value + FilenameEncoding.URL_FILE_SEPARATOR)) { 
    258278            current_fileset_matches = true; 
    259279            folder_metadata_inherited_from = new File(metadata_xml_file_directory, current_filename_element_value); 
     
    272292        Element current_metadata_element = (Element) metadata_elements_nodelist.item(k); 
    273293        String metadata_element_name_full = current_metadata_element.getAttribute("name"); 
     294        // if we're only looking for fileEncoding metadata and this isn't it, skip to the next 
     295        if(fileEncodingOnly && !metadata_element_name_full.equals(FILENAME_ENCODING_METADATA)) { 
     296            continue; 
     297        }        
    274298        String metadata_set_namespace = MetadataTools.getMetadataSetNamespace(metadata_element_name_full); 
    275299 
     
    335359 
    336360 
    337     public void removeMetadata(File file, ArrayList metadata_values) 
     361    public void removeMetadata(CollectionTreeNode file_node, ArrayList metadata_values) 
    338362    { 
    339363    // If this metadata.xml file isn't the one currently loaded, load it now 
     
    354378 
    355379    // Determine the file's path relative to the location of the metadata.xml file 
    356     String metadata_xml_file_directory_path = getParentFile().getAbsolutePath(); 
    357     String file_relative_path = file.getAbsolutePath().substring(metadata_xml_file_directory_path.length()); 
    358     if (file_relative_path.startsWith(File.separator)) { 
    359         file_relative_path = file_relative_path.substring(File.separator.length()); 
     380    String metadata_xml_file_directory_path = FilenameEncoding.fileToURLEncoding(getParentFile()); 
     381    String file_relative_path = file_node.getURLEncodedFilePath().substring(metadata_xml_file_directory_path.length()); 
     382    if (file_relative_path.startsWith(FilenameEncoding.URL_FILE_SEPARATOR)) { 
     383        file_relative_path = file_relative_path.substring(FilenameEncoding.URL_FILE_SEPARATOR.length()); 
    360384    } 
    361385 
     
    422446            String current_metadata_value_string = XMLTools.getElementTextValue(current_metadata_element); 
    423447            if (current_metadata_value_string.equals(metadata_value_string)) { 
     448             
    424449            // Remove this Metadata element 
    425450            current_metadata_element.getParentNode().removeChild(current_metadata_element); 
    426  
     451             
     452            // the gs.filenameEncoding metadata is unique in that, when added, removed or 
     453            // changed, it must be applied on the file(name) whose metadata has been adjusted 
     454            if(current_metadata_element_name_full.equals(FILENAME_ENCODING_METADATA)) { 
     455             
     456                // metadata_value_string will hereafter be the inherited gs.FilenameEncoding  
     457                // metadata (if any), now that the value at this level has been removed 
     458                metadata_value_string = processFilenameEncoding(file_path_regexp, 
     459                        file_node, "", true); // true only if *removing* this meta   
     460            } 
     461             
    427462            // If there are no Metadata elements left now, remove the (empty) FileSet element 
    428463            if (metadata_elements_nodelist.getLength() == 0) { 
     
    441476 
    442477 
    443     public void replaceMetadata(File file, MetadataValue old_metadata_value, MetadataValue new_metadata_value) 
     478    public void replaceMetadata(CollectionTreeNode file_node, MetadataValue old_metadata_value, MetadataValue new_metadata_value) 
    444479    { 
    445480    // If this metadata.xml file isn't the one currently loaded, load it now 
     
    460495 
    461496    // Determine the file's path relative to the location of the metadata.xml file 
    462     String metadata_xml_file_directory_path = getParentFile().getAbsolutePath(); 
    463     String file_relative_path = file.getAbsolutePath().substring(metadata_xml_file_directory_path.length()); 
    464     if (file_relative_path.startsWith(File.separator)) { 
    465         file_relative_path = file_relative_path.substring(File.separator.length()); 
     497    String metadata_xml_file_directory_path = FilenameEncoding.fileToURLEncoding(getParentFile()); 
     498    String file_relative_path = file_node.getURLEncodedFilePath().substring(metadata_xml_file_directory_path.length()); 
     499    if (file_relative_path.startsWith(FilenameEncoding.URL_FILE_SEPARATOR)) { 
     500        file_relative_path = file_relative_path.substring(FilenameEncoding.URL_FILE_SEPARATOR.length()); 
    466501    } 
    467502 
     
    541576        // If the new metadata value already existed, remove the original value 
    542577        if (new_metadata_value_already_exists) { 
    543         metadata_element_to_edit.getParentNode().removeChild(metadata_element_to_edit); 
     578            if(metadata_element_to_edit != null) { //????????? 
     579                metadata_element_to_edit.getParentNode().removeChild(metadata_element_to_edit); 
     580            } else { 
     581                System.err.println("ERROR MetadataXMLFile: metadata_element_to_edit is null"); 
     582            } 
    544583        } 
    545584        // Otherwise replace the old value with the new value 
    546585        // Ensure metadata_element_to_edit isn't null (may occur when multiple files are selected) 
    547586        else if (metadata_element_to_edit != null) { 
     587         
     588        // the gs.filenameEncoding metadata is unique in that, when added, removed or 
     589        // changed, it must be applied on the file(name) whose metadata has been adjusted 
     590        if(metadata_element_name_full.equals(FILENAME_ENCODING_METADATA)) { 
     591            new_metadata_value_string = processFilenameEncoding(file_path_regexp, file_node, new_metadata_value_string, false); 
     592            // true only if removing meta        
     593        }        
    548594        XMLTools.setElementTextValue(metadata_element_to_edit, new_metadata_value_string); 
    549595        } 
     
    649695    } 
    650696    } 
     697 
     698    /**  
     699     * The gs.filenameEncoding metadata is unique in that, when added, removed or 
     700     * replaced, it must be applied on the file(name) whose metadata has been  
     701     * adjusted.  
     702     * This method handles all that, given the regular expression or filepath name  
     703     * to match on (.* matches subdirectories), the affected fileNode, the new  
     704     * encoding value and whether a new encoding value has been added/an existing 
     705     * one has been replaced or whether the encoding metadata has been removed. 
     706     * The new adjusted value for the encoding metadata is returned. 
     707     * 
     708     * MetadataXMLFileManager maintains a hashmap of (URL-encoded filepaths, encoding)  
     709     * to allow fast access to previously assigned gs.filenameEncoding metadata (if 
     710     * any) for each file. This hashmap also needs to be updated, but this update 
     711     * is complicated by the fact that it concerns regular expressions that could 
     712     * affect multiple filenames. 
     713     */ 
     714    public String processFilenameEncoding(String file_path_regexp, CollectionTreeNode file_node,  
     715                    String encoding_metadata_value, boolean removingMetadata)  
     716    { 
     717        if(!FilenameEncoding.MULTIPLE_FILENAME_ENCODINGS_SUPPORTED) { 
     718            return encoding_metadata_value; 
     719        } 
     720 
     721        // Work out this filenode's new encoding and apply it: 
     722         
     723        if(removingMetadata) { // encoding_metadata_value = "" 
     724            // gs.filenameEncoding metadata being removed, work out  
     725            // any inherited metadata to replace it with in the meta-table 
     726            encoding_metadata_value = FilenameEncoding.getInheritedFilenameEncoding( 
     727                        file_node.getURLEncodedFilePath(), file_node.getFile()); 
     728            // should be canonical encoding already 
     729        }  
     730        else if(!encoding_metadata_value.equals("")) {  
     731            // if adding or replacing filename encoding, 
     732            // get the canonical encoding name for this alias 
     733            encoding_metadata_value = FilenameEncoding.canonicalEncodingName(encoding_metadata_value); 
     734        }    
     735        // Reencode the display of this filenode only as any affected  
     736        // childnodes will be reencoded on FileNode.refreshDescendantEncodings() 
     737        file_node.reencodeDisplayName(encoding_metadata_value); 
     738             
     739 
     740        // Whether removing or adding/replacing the file's gs.filename encoding meta, 
     741        // store this in the file-to-encoding map for fast access, since the map stores 
     742        // empty string values when no meta has been assigned at this file level. 
     743        // In the case of removingMetadata, the value stored will be the fallback value 
     744         
     745        String urlpath = file_node.getURLEncodedFilePath(); 
     746        if(removingMetadata) {  
     747            // remove it from the map instead of inserting "", so that when folders in the collectiontree  
     748            // are being deleted or shifted, the removemetada (and addmetadata) calls that get fired  
     749            // for each affected filenodes does not cause the undesirable effect of multiple "" to be  
     750            // entered into the filename-to-encoding map for filepaths that no longer exist . 
     751            FilenameEncoding.map.remove(urlpath);  
     752        } else { // for adding and replacing, put the encoding into the map (also replaces any existing encoding for it) 
     753            FilenameEncoding.map.put(urlpath, encoding_metadata_value);          
     754        } 
     755         
     756        // If new folder-level metadata (or metadata for a set of files fitting a pattern) has been 
     757        //  assigned, the file_to_encodings map will be cleared for all descendant folders and files, 
     758        //  so that these can be re-calculated upon refreshing the visible parts of the CollectionTree.  
     759        // Mark the state as requiring a refresh of the CollectionTree.  
     760        // This next step also serves to prevent the MetadataValueTableModel from trying to update 
     761        // itself while a refresh (involving re-encoding of filenames of visible nodes) is in progress. 
     762        FilenameEncoding.setRefreshRequired(true); 
     763         
     764        return encoding_metadata_value; 
     765    } 
    651766} 
  • main/trunk/gli/src/org/greenstone/gatherer/metadata/MetadataXMLFileManager.java

    r23410 r23433  
    9090        if (current_file_directory_path.equals(metadata_xml_file.getParentFile().getAbsolutePath())) { 
    9191            applicable_metadata_xml_file_found = true; 
    92             metadata_xml_file.addMetadata(current_file, metadata_values); 
     92            metadata_xml_file.addMetadata(file_nodes[i], metadata_values); 
    9393            if (!modified_metadata_xml_files.contains(metadata_xml_file)) { 
    9494            modified_metadata_xml_files.add(metadata_xml_file); 
     
    107107 
    108108        // ...and add the metadata 
    109         new_metadata_xml_file.addMetadata(current_file, metadata_values); 
     109        new_metadata_xml_file.addMetadata(file_nodes[i], metadata_values); 
    110110        if (!modified_metadata_xml_files.contains(new_metadata_xml_file)) { 
    111111            modified_metadata_xml_files.add(new_metadata_xml_file); 
     
    160160 
    161161    // Get the metadata assigned to the specified file from the applicable metadata.xml files 
    162     ArrayList assigned_metadata = getMetadataAssignedToFile(file, applicable_metadata_xml_files); 
     162    ArrayList assigned_metadata = getMetadataAssignedToFile(file, applicable_metadata_xml_files, false); 
    163163 
    164164    // Remove any folder-level metadata 
     
    173173 
    174174 
     175    /** Returns the metadata assigned to a file inside the collection, excluding folder-level/inherited metadata. */ 
     176    static public ArrayList getMetadataAssignedDirectlyToFile(File file) 
     177    { 
     178        return getMetadataAssignedDirectlyToFile(file, false); 
     179    } 
     180     
    175181    /** Returns the metadata assigned to a file inside the collection, excluding folder-level/inherited metadata. */ 
    176     static public ArrayList getMetadataAssignedDirectlyToFile(File file) 
    177     { 
    178     // Get all the metadata assigned to the specified file... 
    179     ArrayList assigned_metadata = getMetadataAssignedToFile(file); 
    180  
    181     // ...then remove any folder-level metadata 
    182     for (int i = assigned_metadata.size() - 1; i >= 0; i--) { 
    183         if (((MetadataValue) assigned_metadata.get(i)).isInheritedMetadata()) { 
    184         assigned_metadata.remove(i); 
    185         } 
    186     } 
    187  
    188     return assigned_metadata; 
    189     } 
    190  
     182    static public ArrayList getMetadataAssignedDirectlyToFile(File file, boolean filenameEncodingMetaOnly) 
     183    { 
     184         
     185        // Get all the metadata assigned to the specified file... 
     186        ArrayList assigned_metadata = getMetadataAssignedToFile(file, filenameEncodingMetaOnly); 
     187 
     188        // ...then remove any folder-level metadata 
     189        for (int i = assigned_metadata.size() - 1; i >= 0; i--) { 
     190            if (((MetadataValue) assigned_metadata.get(i)).isInheritedMetadata()) { 
     191            assigned_metadata.remove(i); 
     192            } 
     193        } 
     194 
     195        return assigned_metadata; 
     196        /* 
     197        // Get all the metadata assigned to the specified file... 
     198        // Build up a list of applicable metadata.xml files - which in this case  
     199        // is exclusively the metadata file at this file/folder's own level. 
     200        ArrayList applicable_metadata_xml_files = new ArrayList(); 
     201 
     202        // Find the metadata.xml file (if any) that is at the same level as the file 
     203        String file_directory_path = (file.isDirectory() ? file : file.getParentFile()).getAbsolutePath() + File.separator; 
     204        for (int i = 0; i < metadata_xml_files.size(); i++) { 
     205            MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(i); 
     206             
     207            if (file_directory_path.equals(metadata_xml_file.getParentFile().getAbsolutePath() + File.separator)) { 
     208                //System.err.println("Found metadata_xml_file: " + metadata_xml_file); 
     209                applicable_metadata_xml_files.add(metadata_xml_file); 
     210            } 
     211        } 
     212         
     213        if(applicable_metadata_xml_files.size() == 0) { 
     214            return new ArrayList(0); 
     215        } 
     216 
     217        // Return the metadata assigned to the specified file from the applicable metadata.xml files 
     218        return getMetadataAssignedToFile(file, applicable_metadata_xml_files, filenameEncodingMetaOnly); 
     219        */ 
     220    } 
     221     
    191222 
    192223    /** Returns all the metadata assigned to a file inside the collection. */ 
    193224    static public ArrayList getMetadataAssignedToFile(File file) 
     225    { 
     226    return getMetadataAssignedToFile(file, false); 
     227    } 
     228     
     229    /** Returns all the metadata assigned to a file inside the collection (or  
     230     *  just gs.filenameEncoding if parameter filenameEncodingMetaOnly is true), 
     231     *  including folder-level/inherited metadata. 
     232     */     
     233    static public ArrayList getMetadataAssignedToFile(File file, boolean filenameEncodingMetaOnly) 
    194234    { 
    195235    // Build up a list of applicable metadata.xml files 
     
    207247    } 
    208248 
    209     // sort the metadataxml files in order starting from those in the  
     249    // Sort the metadataxml files in order starting from those in the  
    210250    // topmost folders down to the one in the lowest level folder. 
    211251    Collections.sort(applicable_metadata_xml_files, metadataXMLFileComparator); 
    212252    // Return the metadata assigned to the specified file from the applicable metadata.xml files 
    213     return getMetadataAssignedToFile(file, applicable_metadata_xml_files); 
    214     } 
    215  
    216  
    217     static private ArrayList getMetadataAssignedToFile(File file, ArrayList applicable_metadata_xml_files) 
     253    return getMetadataAssignedToFile(file, applicable_metadata_xml_files, filenameEncodingMetaOnly); 
     254    } 
     255 
     256    // package access method 
     257    static ArrayList getMetadataAssignedToFile(File file, ArrayList applicable_metadata_xml_files, 
     258                               boolean filenameEncodingMetaOnly)     
    218259    { 
    219260    // Build up a list of metadata values assigned to this file 
     
    225266        DebugStream.println("Applicable metadata.xml file: " + metadata_xml_file); 
    226267 
    227         ArrayList metadata_values = metadata_xml_file.getMetadataAssignedToFile(file); 
     268        ArrayList metadata_values = metadata_xml_file.getMetadataAssignedToFile(file, filenameEncodingMetaOnly); 
    228269        for (int j = 0; j < metadata_values.size(); j++) { 
    229270        MetadataValue metadata_value = (MetadataValue) metadata_values.get(j); 
     
    316357        // This metadata.xml file is only potentially applicable if it is above or at the same level as the file 
    317358        if (current_file_directory_path.startsWith(metadata_xml_file.getParentFile().getAbsolutePath())) { 
    318             metadata_xml_file.removeMetadata(current_file, metadata_values); 
     359            metadata_xml_file.removeMetadata(file_nodes[i], metadata_values); 
    319360            if (!modified_metadata_xml_files.contains(metadata_xml_file)) { 
    320361            modified_metadata_xml_files.add(metadata_xml_file); 
     
    350391        // This metadata.xml file is only applicable if it is at the same level as the file 
    351392        if (current_file_directory_path.equals(metadata_xml_file.getParentFile().getAbsolutePath())) { 
    352             metadata_xml_file.replaceMetadata(current_file, old_metadata_value, new_metadata_value); 
     393            metadata_xml_file.replaceMetadata(file_nodes[i], old_metadata_value, new_metadata_value); 
    353394            if (!modified_metadata_xml_files.contains(metadata_xml_file)) { 
    354395            modified_metadata_xml_files.add(metadata_xml_file); 
     
    399440 
    400441   
    401   /** 
    402   * Comparator to order MetadataXMLFiles in ascending order from 
    403   * those in a higher level folder to those in a lower level folder 
    404   * It is based on the assumption that all MetadataXMLFiles sent to 
    405   * it to compare will be linear descendants of one toplevel folder 
    406   * E.g. /A/metadata.xml, /A/B/metadata.xml, /A/B/C/D/metadata.xml. 
    407   * In other words, that each is a substring of one other until we 
    408   * get to the toplevel folder. 
    409   */ 
    410   private static class MetadataXMLFileComparator implements Comparator { 
    411      
    412     public int compare(Object o1, Object o2) { 
    413       if(!(o1 instanceof MetadataXMLFile)) { 
    414     return -1; 
    415       } else if (!(o2 instanceof MetadataXMLFile)) { 
    416     return 1; 
    417       }  
    418  
    419       // Both are MetadataXMLFiles objects. Remove the terminating 
    420       // "metadata.xml" from their filenames to get their containing folder 
    421       String filename1 = ((MetadataXMLFile)o1).getParentFile().getAbsolutePath(); 
    422       String filename2 = ((MetadataXMLFile)o2).getParentFile().getAbsolutePath(); 
    423  
    424       // if 1 is a prefix 2, then 1 < 2 in the ordering (1 comes before 2) 
    425       if(filename2.startsWith(filename1)) { 
    426     return -1; 
    427       } else if(filename1.startsWith(filename2)) { 
    428     return 1; 
    429       } else {  
    430     // unlikely that the metadata.xml files will be the same 
    431     // or that neither is a prefix of the other 
    432     return filename1.compareTo(filename2); // sorts in ascending order 
    433       }       
    434        
    435     } 
    436  
    437     public boolean equals(Object obj) { 
    438       if(!(obj instanceof MetadataXMLFileComparator)) { 
    439     return false; 
    440       }  
    441       
    442       // else it is the same sort of comparator 
    443       return true; 
    444     } 
    445  
    446   } 
     442    /** 
     443      * Comparator to order MetadataXMLFiles in ascending order from 
     444      * those in a higher level folder to those in a lower level folder 
     445      * It is based on the assumption that all MetadataXMLFiles sent to 
     446      * it to compare will be linear descendants of one toplevel folder 
     447      * E.g. /A/metadata.xml, /A/B/metadata.xml, /A/B/C/D/metadata.xml. 
     448      * In other words, that each is a substring of one of the others until we 
     449      * the toplevel folder is reached. 
     450    */ 
     451    private static class MetadataXMLFileComparator implements Comparator { 
     452         
     453        public int compare(Object o1, Object o2) { 
     454            if(!(o1 instanceof MetadataXMLFile)) { 
     455                return -1; 
     456            } else if (!(o2 instanceof MetadataXMLFile)) { 
     457                return 1; 
     458            }  
     459 
     460            // Both are MetadataXMLFiles objects. Remove the terminating 
     461            // "metadata.xml" from their filenames to get their containing folder 
     462            String filename1 = ((MetadataXMLFile)o1).getParentFile().getAbsolutePath(); 
     463            String filename2 = ((MetadataXMLFile)o2).getParentFile().getAbsolutePath(); 
     464 
     465            // if 1 is a prefix of 2, then 1 < 2 in the ordering (1 comes before 2) 
     466            if(filename2.startsWith(filename1)) { 
     467                return -1; 
     468            } else if(filename1.startsWith(filename2)) { 
     469                return 1; 
     470            } else {  
     471                // unlikely that the metadata.xml files will be the same 
     472                // or that neither is a prefix of the other 
     473                return filename1.compareTo(filename2); // sorts in ascending order 
     474            }       
     475 
     476        } 
     477 
     478        public boolean equals(Object obj) { 
     479            if(!(obj instanceof MetadataXMLFileComparator)) { 
     480                return false; 
     481            }  
     482 
     483            // else it is the same sort of comparator 
     484            return true; 
     485        } 
     486 
     487    } 
    447488 
    448489} 
  • main/trunk/gli/src/org/greenstone/gatherer/util/SynchronizedTreeModelTools.java

    r11084 r23433  
    3131import javax.swing.tree.*; 
    3232import org.greenstone.gatherer.DebugStream; 
     33import org.greenstone.gatherer.metadata.FilenameEncoding; 
     34import org.greenstone.gatherer.collection.CollectionTreeNode; 
    3335 
    3436/** Due to the TreeModel objects not having any synchronization, certain assumptions, such as the model state remaining constant during a repaint, don't always hold - especially given that I'm changing the tree model on a different thread. In order to get around this I will use the latest swing paradigm wherein you flag a section of code to be executed by the AWT GUI Event queue, as soon as other gui tasks have finished. This way I shouldn't have tree redraws throwing NPEs because the array size of the children of a certain node has changed -while- the repaint call was made, i.e. repaint() calls getChildCount() = 13, removeNodeFromParent() called, repaint calls getChildAt(12) = ArrayIndexOutOfBoundsException.  
     
    104106    final Runnable doRemoveNodeFromParent = new Runnable() { 
    105107        public void run() { 
    106             model.removeNodeFromParent(target_node); 
     108            // If we're dealing with a collection tree node, it may have  
     109            // gs.FilenameEncoding assigned, so we remove its entry from the map.  
     110            // Needs to be done here because the tree is constantly changing  
     111            // when nodes are being removed, renamed and deleted, and this  
     112            // affects lookup queries sent to the map.  
     113            // Don't need to do a recursive reset on this coltreenode, because  
     114            // Delete/Move/Rename FileJobs were created for *each* node          
     115            if(target_node instanceof CollectionTreeNode) { 
     116                CollectionTreeNode colNode = (CollectionTreeNode)target_node; 
     117                FilenameEncoding.map.remove(colNode.getURLEncodedFilePath()); 
     118            } 
     119             
     120            model.removeNodeFromParent(target_node);             
    107121        } 
    108122        };