Changeset 23433


Ignore:
Timestamp:
12/09/10 22:27:33 (11 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 edited

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        };
Note: See TracChangeset for help on using the changeset viewer.