Changeset 9856


Ignore:
Timestamp:
2005-05-12T12:24:38+12:00 (19 years ago)
Author:
mdewsnip
Message:

Major changes to the metadata value table and tree in the Enrich pane. The metadata value table now allows direct editing in the table -- hopefully much more obvious to the user. Classes involved have been untangled and are much more re-usable. Special code for replacing metadata has been added, which uses a more direct and efficient method rather than removing then adding metadata as before. And many many minor tidy-ups.

Location:
trunk/gli/src/org/greenstone/gatherer
Files:
3 added
1 deleted
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/gli/src/org/greenstone/gatherer/collection/CollectionManager.java

    r9828 r9856  
    5959import org.greenstone.gatherer.gui.WarningDialog;
    6060import org.greenstone.gatherer.metadata.DocXMLFileManager;
     61import org.greenstone.gatherer.metadata.MetadataChangedListener;
    6162import org.greenstone.gatherer.metadata.MetadataSet;
    6263import org.greenstone.gatherer.metadata.MetadataSetManager;
     
    7879 */
    7980public class CollectionManager
    80     implements GShellListener {
    81 
     81    implements GShellListener, MetadataChangedListener
     82{
    8283    /** Are we currently in the process of building? */
    8384    private boolean building = false;
     
    111112    this.importing = false;
    112113    this.collection = null;
     114
     115    MetadataXMLFileManager.addMetadataChangedListener(this);
    113116    }
    114117
     
    947950    public synchronized void message(GShellEvent event) {
    948951    }
     952
     953
     954    public void metadataChanged(CollectionTreeNode[] file_nodes)
     955    {
     956    if (collection != null) {
     957        collection.setMetadataChanged(true);
     958    }
     959    }
     960
    949961
    950962    /** This call is fired whenever a process within a GShell created by this class begins.
  • trunk/gli/src/org/greenstone/gatherer/gui/EditorDialog.java

    r8994 r9856  
    6262    private String result = null;
    6363    /** The size of the edit pop-up. */
    64     final static private Dimension SIZE = new Dimension(400,425);
     64    final static private Dimension SIZE = new Dimension(400, 300);
    6565
    6666    /** Constructor */
  • trunk/gli/src/org/greenstone/gatherer/gui/EnrichPane.java

    r9350 r9856  
    11/**
    2  *#########################################################################
    3  *
    4  * A component of the Gatherer application, part of the Greenstone digital
    5  * library suite from the New Zealand Digital Library Project at the
     2 *############################################################################
     3 * A component of the Greenstone Librarian Interface, part of the Greenstone
     4 * digital library suite from the New Zealand Digital Library Project at the
    65 * University of Waikato, New Zealand.
    76 *
    8  * <BR><BR>
     7 * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
     8 * Based on code by John Thompson
    99 *
    10  * Author: John Thompson, Greenstone Digital Library, University of Waikato
    11  *
    12  * <BR><BR>
    13  *
    14  * Copyright (C) 1999 New Zealand Digital Library Project
    15  *
    16  * <BR><BR>
     10 * Copyright (C) 2005 New Zealand Digital Library Project
    1711 *
    1812 * This program is free software; you can redistribute it and/or modify
     
    2115 * (at your option) any later version.
    2216 *
    23  * <BR><BR>
    24  *
    2517 * This program is distributed in the hope that it will be useful,
    2618 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     
    2820 * GNU General Public License for more details.
    2921 *
    30  * <BR><BR>
    31  *
    3222 * You should have received a copy of the GNU General Public License
    3323 * along with this program; if not, write to the Free Software
    3424 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    35  *########################################################################
     25 *############################################################################
    3626 */
     27
    3728package org.greenstone.gatherer.gui;
    3829
     
    4435import javax.swing.*;
    4536import javax.swing.event.*;
    46 import javax.swing.table.*;
     37import javax.swing.text.*;
    4738import javax.swing.tree.*;
    4839import org.greenstone.gatherer.Configuration;
     
    5647import org.greenstone.gatherer.metadata.MetadataValue;
    5748import org.greenstone.gatherer.metadata.MetadataValueTableEntry;
    58 import org.greenstone.gatherer.metadata.MetadataValueTableModel;
    59 import org.greenstone.gatherer.metadata.MetadataValueTreeModel;
    6049import org.greenstone.gatherer.metadata.MetadataValueTreeNode;
    61 import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
    6250import org.greenstone.gatherer.util.DragGroup;
    63 import org.greenstone.gatherer.util.PatternTokenizer;
    6451import org.greenstone.gatherer.util.TreeSynchronizer;
    65 import org.greenstone.gatherer.util.Utility;
    66 import org.greenstone.gatherer.util.XMLTools;
    6752
    6853
     
    7156public class EnrichPane
    7257    extends JPanel
    73     implements ActionListener, TreeSelectionListener
     58    implements TreeSelectionListener
    7459{
    75     static private Dimension CONTROL_SIZE = new Dimension(560, 240);
    7660    static private Dimension MINIMUM_SIZE = new Dimension(100, 100);
    77     static private Dimension TABLE_SIZE = new Dimension(560, 25);
    78     static private Dimension TREE_SIZE = new Dimension(250, 500);
    79     static private int MINIMUM_TABLE_HEADER_SIZE = 15;
    80 
    81     private MetadataValueTable metadata_value_table = null;
    82     /** The layout manager used to display either the MetadataValueTable (when files are selected) or a placeholder panel */
    83     private CardLayout metadata_value_table_card_layout = null;
    84     /** The name of the panel containing the metadata table */
    85     static final private String TABLE_CARD = "";
    86     /** The name of the panel containing the "no files selected" placeholder for the metadata table */
    87     static final private String TABLE_CARD_NO_FILES_SELECTED = "No files selected";
    88     /** The name of the panel containing the "no metadata available" placeholder for the metadata table */
    89     static final private String TABLE_CARD_NO_METADATA_AVAILABLE = "No metadata available";
    90 
    91     private JTextField metadata_value_text_field = null;
    92     /** The MetadataValueTree graphically shows the available metadata that has been previously assigned, and provides controls for adding, updating and removing metadata, depending on the user's selections in the other important components. */
    93     private MetadataValueTree metadata_value_tree = null;
    94     /** The layout manager used to display either the MetadataValueTree (when metadata is selected) or a placeholder panel */
    95     private CardLayout metadata_value_tree_card_layout = null;
    96     /** The name of the panel containing the metadata value tree */
    97     static final private String TREE_CARD = "";
    98     /** The name of the panel containing the "no metadata element selected" placeholder for the metadata table */
    99     static final private String TREE_CARD_NO_METADATA_ELEMENT_SELECTED  = "No metadata element selected";
    100 
    101     /** Used to dynamically filter the collection tree at user request. Note that this is synchronized with the collection tree filter in the Gather view. */
    102     private Filter collection_filter = null;
     61    static private Dimension COLLECTION_TREE_SIZE = new Dimension(250, 500);
     62
     63    /** The collection tree. */
     64    private CollectionTree collection_tree = null;
    10365    /** The currently reported selection. */
    10466    private CollectionTreeNode[] file_nodes = null;
    105     /** The button, which when clicked, adds metadata to the selected records. */
    106     private JButton add;
    107     /** The button, which when clicked, removes the selected metadata from the selected records. */
    108     private JButton remove;
    109     /** The button, which when clicked, updates the selected metadata from the selected records. */
    110     private JButton replace;
    111     /** This button creates an editor dialog to allow for an expanded area to type in text. */
    112     private JButton expand;
    113     private JButton expand_for_extracted;
     67    /** Used to dynamically filter the collection tree. Synchronized with the same filter in the Gather view. */
     68    private Filter collection_filter = null;
    11469    /** The label at the top of the collection tree, which shows the collection name. */
    11570    private JLabel collection_label;
    116     /** The panel in which the metadata value card layout resides. */
    117     private JPanel control_pane;
    118     /** The panel in which the metadata table card layout resides. */
    119     private JPanel table_card_pane;
    120     /** The splitpane dividing the collection tree and the metadata based controls. */
     71    /** The splitpane dividing the collection tree and the metadata editing controls. */
    12172    private JSplitPane external_split;
    122     /** The label shown at the top of the metadata table detailing the current selection statistics. */
    123     private JTextField table_label;
    124     /** A reference to the collection tree. */
    125     private CollectionTree collection_tree;
     73    /** The metadata value table shows the metadata values that are currently assigned to a file. */
     74    private MetadataValueTablePane metadata_value_table_pane = null;
     75    /** The metadata value tree shows the metadata values that are currently assigned to a metadata element. */
     76    private MetadataValueTreePane metadata_value_tree_pane = null;
    12677    /** Provide synchronization between the collection trees in this view and the collection pane view. */
    12778    private TreeSynchronizer collection_tree_sync = null;
    128     private boolean metadata_edit_event = false;
    12979
    13080
     
    13686    this.collection_tree_sync = collection_tree_sync;
    13787
    138     add = new GLIButton();
    139     add.addActionListener(this);
    140     add.setEnabled(false);
    141     add.setMnemonic(KeyEvent.VK_A);
    142     add.setPreferredSize(Utility.MINIMUM_BUTTON_SIZE);
    143     Dictionary.registerBoth(add, "EnrichPane.Accumulate", "EnrichPane.Accumulate_Tooltip");
    144 
    145     replace = new GLIButton();
    146     replace.addActionListener(this);
    147     replace.setEnabled(false);
    148     replace.setMnemonic(KeyEvent.VK_P);
    149     replace.setPreferredSize(Utility.MINIMUM_BUTTON_SIZE);
    150     Dictionary.registerBoth(replace, "EnrichPane.Overwrite", "EnrichPane.Overwrite_Tooltip");
    151 
    152     remove = new GLIButton();
    153     remove.addActionListener(this);
    154     remove.setEnabled(false);
    155     remove.setMnemonic(KeyEvent.VK_R);
    156     remove.setPreferredSize(Utility.MINIMUM_BUTTON_SIZE);
    157     Dictionary.registerBoth(remove, "EnrichPane.Remove", "EnrichPane.Remove_Tooltip");
    158 
    159     expand = new GLIButton();
    160     expand.addActionListener(this);
    161     expand.setEnabled(true);
    162     expand.setMnemonic(KeyEvent.VK_E);
    163     expand.setPreferredSize(new Dimension(25, 25));
    164     Dictionary.registerBoth(expand, "EnrichPane.Expand", "EnrichPane.Expand_Tooltip");
    165 
    166     expand_for_extracted = new GLIButton();
    167     expand_for_extracted.addActionListener(this);
    168     expand_for_extracted.setEnabled(true);
    169     expand_for_extracted.setMnemonic(KeyEvent.VK_E);
    170     expand_for_extracted.setPreferredSize(new Dimension(25, 25));
    171     Dictionary.registerBoth(expand_for_extracted, "EnrichPane.Expand", "EnrichPane.Expand_Tooltip");
    172 
    173     metadata_value_text_field = new JTextField();
    174     metadata_value_table = new MetadataValueTable();
    175     metadata_value_tree = new MetadataValueTree(CONTROL_SIZE.width, CONTROL_SIZE.height);
    176     }
    177 
    178 
    179     /** Called whenever an action occurs on one of our registered buttons.
    180      * @param event An <strong>ActionEvent</strong> containing information about the event.
    181      */
    182     public void actionPerformed(ActionEvent event)
    183     {
    184     Object esrc = event.getSource();
    185 
    186     // One of the metadata editing buttons was pressed
    187     if (esrc == add || esrc == replace || esrc == remove) {
    188         // Double-check that some files and a metadata element have been selected
    189         MetadataValueTableEntry selected_metadata_value_table_entry = metadata_value_table.getSelectedMetadataValueTableEntry();
    190         if (file_nodes == null || selected_metadata_value_table_entry == null) {
    191         return;
    192         }
    193 
    194         // If we're adding metadata to folders display the warning
    195         if (esrc == add && !file_nodes[0].isLeaf()) {
    196         WarningDialog dialog = new WarningDialog("warning.DirectoryLevelMetadata", "DirectoryLevelMetadata.Title", "DirectoryLevelMetadata.Message", null, true);
    197         int dialog_result = dialog.display();
    198         dialog.dispose();
    199 
    200         if (dialog_result != JOptionPane.OK_OPTION) {
    201             return;
    202         }
    203         }
    204 
    205         // Lock the interface so nothing can be changed while the metadata edit is being processed
    206         Gatherer.g_man.wait(true);
    207 
    208         // Add button pressed
    209         if (esrc == add) {
    210         MetadataElement metadata_element = metadata_value_table.getSelectedMetadataElement();
    211         MetadataValueTreeNode metadata_value_tree_node = metadata_element.addMetadataValue(metadata_value_tree.getSelectedValue());
    212         MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
    213         metadata_value.setIsAccumulatingMetadata(true);
    214         (new AppendMetadataTask(metadata_value)).run();
    215         }
    216 
    217         // Replace button pressed
    218         else if (esrc == replace) {
    219         MetadataElement metadata_element = metadata_value_table.getSelectedMetadataElement();
    220         MetadataValueTreeNode metadata_value_tree_node = metadata_element.addMetadataValue(metadata_value_tree.getSelectedValue());
    221         MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
    222         metadata_value.setIsAccumulatingMetadata(!metadata_value_table.getSelectedMetadataValueTableEntry().isInheritedMetadata());
    223         (new ReplaceMetadataTask(metadata_value, selected_metadata_value_table_entry)).run();
    224         }
    225 
    226         // Remove button pressed
    227         else if (esrc == remove) {
    228         (new RemoveMetadataTask(selected_metadata_value_table_entry)).run();
    229         }
    230 
    231         // Note that the collection's metadata has been changed
    232         Gatherer.c_man.getCollection().setMetadataChanged(true);
    233     }
    234 
    235     // Expand button pressed
    236     else if (esrc == expand) {
    237         EditorDialog ed = new EditorDialog();
    238         String new_metadata_value = ed.display(metadata_value_tree.getSelectedValue());
    239         if (new_metadata_value != null) {
    240         metadata_value_tree.setSelectedValue(new_metadata_value);
    241         }
    242     }
    243 
    244     // Expand button for extracted metadata pressed
    245     else if (esrc == expand_for_extracted) {
    246         EditorDialog ed = new EditorDialog();
    247         ed.setEditable(false);
    248         ed.display(metadata_value_table.getSelectedMetadataValueTableEntry().getFullValue());
    249     }
     88    // Create the metadata value tree pane
     89    metadata_value_tree_pane = new MetadataValueTreePane();
     90    metadata_value_tree_pane.addMetadataValueTreeSelectionListener(new MetadataValueTreeSelectionListener());
     91
     92    // Create metadata value table pane
     93    metadata_value_table_pane = new MetadataValueTablePane();
     94    metadata_value_table_pane.addMetadataValueTableListSelectionListener(new MetadataValueTableListSelectionListener());
     95    metadata_value_table_pane.addMetadataValueTableMouseListener(new MetadataValueTableMouseListener());
     96    metadata_value_table_pane.addMetadataValueTextFieldDocumentListener(new MetadataValueTextFieldDocumentListener());
     97    metadata_value_table_pane.addMetadataValueTextFieldKeyListener(new MetadataValueTextFieldKeyListener());
    25098    }
    25199
     
    269117    JPanel collection_pane = new JPanel(new BorderLayout());
    270118    collection_pane.setMinimumSize(MINIMUM_SIZE);
    271     collection_pane.setPreferredSize(TREE_SIZE);
     119    collection_pane.setPreferredSize(COLLECTION_TREE_SIZE);
    272120
    273121    external_split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
     
    311159    collection_pane.add(collection_filter, BorderLayout.SOUTH);
    312160
    313     JSplitPane main_split_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
    314     main_split_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    315     main_split_pane.setDividerSize(8);
    316 
    317     // Metadata Table. In order to achieve what Ian envisions I'm going
    318     // to have to do a tricky. The table itself will sit in a stacked
    319     // CardLayout. Card zero will be a see-through JPanel with a message
    320     // smack bang in the center, something like "No Metadata". If any
    321     // page of the table would appear to have no metadata this card will
    322     // be swapped to the front.
    323 
    324     JPanel table_pane = new JPanel();
    325     table_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
    326 
    327     JPanel table_title_pane = new JPanel();
    328 
    329     table_label = new JTextField();
    330     table_label.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
    331     table_label.setEditable(false);
    332     Dictionary.registerText(table_label, "EnrichPane.No_File");
    333 
    334     table_card_pane = new JPanel();
    335 
    336     JPanel table_pane_zero = new JPanel();
    337     table_pane_zero.setOpaque(false);
    338 
    339     JPanel table_pane_two = new JPanel();
    340     table_pane_two.setOpaque(false);
    341 
    342     JLabel no_file_message = new JLabel();
    343     no_file_message.setHorizontalAlignment(JLabel.CENTER);
    344     no_file_message.setOpaque(false);
    345     no_file_message.setVerticalAlignment(JLabel.CENTER);
    346     Dictionary.registerText(no_file_message, "EnrichPane.No_File");
    347 
    348     JLabel no_metadata_message = new JLabel();
    349     no_metadata_message.setHorizontalAlignment(JLabel.CENTER);
    350     no_metadata_message.setOpaque(false);
    351     no_metadata_message.setVerticalAlignment(JLabel.CENTER);
    352     Dictionary.registerText(no_metadata_message, "EnrichPane.No_Metadata");
    353 
    354     JPanel table_pane_one = new JPanel();
    355 
    356     JScrollPane table_scroll = new JScrollPane(metadata_value_table);
    357     table_scroll.getViewport().setBackground(Configuration.getColor("coloring.collection_tree_background", false));
    358     table_scroll.setOpaque(true);
    359 
    360     // Create the cards for the metadata value table
    361     metadata_value_table_card_layout = new CardLayout();
    362 
    363     table_pane_zero.setLayout(new BorderLayout());
    364     table_pane_zero.add(no_file_message, BorderLayout.CENTER);
    365 
    366     table_pane_one.setLayout(new BorderLayout());
    367     table_pane_one.add(table_scroll, BorderLayout.CENTER);
    368 
    369     table_pane_two.setLayout(new BorderLayout());
    370     table_pane_two.add(no_metadata_message, BorderLayout.CENTER);
    371 
    372     table_card_pane.setLayout(metadata_value_table_card_layout);
    373     table_card_pane.add(table_pane_zero, TABLE_CARD_NO_FILES_SELECTED);
    374     table_card_pane.add(table_pane_one, TABLE_CARD);
    375     table_card_pane.add(table_pane_two, TABLE_CARD_NO_METADATA_AVAILABLE);
    376 
    377     // Create the cards for the metadata value tree
    378     metadata_value_tree_card_layout = new CardLayout();
    379 
    380     // Control pane
    381     control_pane = new JPanel();
    382     control_pane.setBorder(BorderFactory.createLoweredBevelBorder());
    383     control_pane.setMinimumSize(MINIMUM_SIZE);
    384     control_pane.setSize(CONTROL_SIZE);
    385     control_pane.setPreferredSize(CONTROL_SIZE);
    386 
    387     JPanel tools_off_pane = new JPanel();
    388 
    389     JLabel tools_off_label = new JLabel();
    390     tools_off_label.setHorizontalAlignment(JLabel.CENTER);
    391     tools_off_label.setOpaque(false);
    392     tools_off_label.setVerticalAlignment(JLabel.CENTER);
    393     Dictionary.registerText(tools_off_label, "EnrichPane.No_Metadata_Element");
    394 
    395     JPanel tools_on_pane = new JPanel();
    396     tools_on_pane.setMinimumSize(MINIMUM_SIZE);
    397     tools_on_pane.setSize(TABLE_SIZE);
    398     tools_on_pane.setPreferredSize(TABLE_SIZE);
    399 
    400     // Layout.
    401 
    402     // Metaedit controls
    403     tools_off_pane.setLayout(new BorderLayout());
    404     tools_off_pane.add(tools_off_label, BorderLayout.CENTER);
    405 
    406     tools_on_pane.setLayout(new BorderLayout());
    407     tools_on_pane.add(metadata_value_tree, BorderLayout.CENTER);
    408 
    409     control_pane.setLayout(metadata_value_tree_card_layout);
    410     control_pane.add(tools_off_pane, TREE_CARD_NO_METADATA_ELEMENT_SELECTED);
    411     control_pane.add(tools_on_pane, TREE_CARD);
    412 
    413     // Table components
    414     table_title_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    415     table_title_pane.setLayout(new BorderLayout());
    416     table_title_pane.add(table_label, BorderLayout.CENTER);
    417 
    418     table_pane.setLayout(new BorderLayout());
    419     table_pane.add(table_title_pane, BorderLayout.NORTH);
    420     table_pane.add(table_card_pane, BorderLayout.CENTER);
    421 
    422     main_split_pane.add(table_pane, JSplitPane.TOP);
    423     main_split_pane.add(control_pane, JSplitPane.BOTTOM);
    424     main_split_pane.setDividerLocation(250);
     161    JSplitPane metadata_editing_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
     162    metadata_editing_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
     163    metadata_editing_pane.setDividerSize(8);
     164
     165    metadata_editing_pane.add(metadata_value_table_pane, JSplitPane.TOP);
     166    metadata_editing_pane.add(metadata_value_tree_pane, JSplitPane.BOTTOM);
     167    metadata_editing_pane.setDividerLocation(250);
    425168
    426169    external_split.add(collection_pane, JSplitPane.LEFT);
    427     external_split.add(main_split_pane, JSplitPane.RIGHT);
     170    external_split.add(metadata_editing_pane, JSplitPane.RIGHT);
    428171
    429172    this.setLayout(new BorderLayout());
     
    432175
    433176
    434     /** Called to inform this control panel that it has just gained focus as an effect of the user clicking on its tab.
     177    /** Called to inform this pane that it has just gained focus as an effect of the user clicking on its tab
    435178     */
    436179    public void gainFocus()
     
    450193
    451194    // Force all of the controls to be updated
    452     valueChanged(new TreeSelectionEvent(this, null, false, null, null));
     195    valueChanged(null);
    453196    }
    454197
     
    502245    // Force the metadata table to be rebuilt (for switching extracted metadata on or off)
    503246    if (refresh_reason == Gatherer.PREFERENCES_CHANGED) {
    504         valueChanged(null);
    505     }
    506     }
    507 
    508 
    509     /** This method is called whenever the selection within the collection
    510      * tree changes. This causes the table to be rebuilt with a new model.
    511      */
     247        metadata_value_table_pane.stopEditingAndRebuild(file_nodes);
     248    }
     249    }
     250
     251
     252    /** Called whenever the collection tree selection changes. This causes the metadata value table to be rebuilt. */
    512253    public void valueChanged(TreeSelectionEvent event)
    513254    {
     
    515256    if (collection_tree.getSelectionCount() == 0) {
    516257        file_nodes = null;
    517 
    518         // Update the label to show nothing is selected in the collection tree
    519         Dictionary.registerText(table_label, "EnrichPane.No_File");
    520 
    521         // Display the "no file selected" card
    522         metadata_value_table_card_layout.show(table_card_pane, TABLE_CARD_NO_FILES_SELECTED);
    523258    }
    524259
     
    530265        file_nodes[i] = (CollectionTreeNode) paths[i].getLastPathComponent();
    531266        }
    532 
    533         // Update the label to show what is selected in the collection tree
    534         table_label.setText(collection_tree.getSelectionDetails());
    535267    }
    536268
    537269    // Update the metadata value table (and consequently, the metadata value tree)
    538     MetadataValue previously_selected_metadata_value = metadata_value_table.getSelectedMetadataValueTableEntry();
    539     metadata_value_table.rebuildAndSelectRowWithValueClosestTo(previously_selected_metadata_value);
    540     }
    541 
    542 
    543     private class AppendMetadataTask
    544     extends Thread
    545     {
    546     private MetadataValue metadata_value = null;
    547 
    548     private AppendMetadataTask(MetadataValue metadata_value)
    549     {
    550         this.metadata_value = metadata_value;
    551     }
    552 
    553     public void run()
     270    metadata_value_table_pane.stopEditingAndRebuild(file_nodes);
     271    }
     272
     273
     274    private class MetadataValueTableListSelectionListener
     275    implements ListSelectionListener
     276    {
     277    public void valueChanged(ListSelectionEvent list_selection_event)
     278    {
     279        // We only want to handle one event per selection, so wait for the value to stabilise
     280        if (list_selection_event.getValueIsAdjusting()) {
     281        return;
     282        }
     283
     284        // Update the metadata value tree for the current table selection
     285        metadata_value_tree_pane.rebuild(metadata_value_table_pane.getSelectedMetadataValueTableEntry());
     286    }
     287    }
     288
     289
     290    private class MetadataValueTableMouseListener
     291    extends MouseAdapter
     292    {
     293    public void mouseClicked(MouseEvent mouse_event)
     294    {
     295        // We're only interested in clicks on the inherited column
     296        if (metadata_value_table_pane.isMouseEventForInheritedMetadataValueTableColumn(mouse_event) == false) {
     297        return;
     298        }
     299
     300        // If the selected metadata is inherited, switch to the folder it came from
     301        MetadataValueTableEntry selected_metadata_value_table_entry = metadata_value_table_pane.getSelectedMetadataValueTableEntry();
     302        if (selected_metadata_value_table_entry.isInheritedMetadata()) {
     303        collection_tree.setSelection(selected_metadata_value_table_entry.getFolderMetadataInheritedFrom());
     304        }
     305    }
     306    }
     307
     308
     309    private class MetadataValueTextFieldDocumentListener
     310    implements DocumentListener
     311    {
     312    /** Gives notification that an attribute or set of attributes changed */
     313    public void changedUpdate(DocumentEvent document_event) {
     314        validate(document_event);
     315    }
     316
     317    /** Gives notification that there was an insert into the document */
     318    public void insertUpdate(DocumentEvent document_event) {
     319        validate(document_event);
     320    }
     321
     322    /** Gives notification that a portion of the document has been removed */
     323    public void removeUpdate(DocumentEvent document_event) {
     324        validate(document_event);
     325    }
     326
     327
     328    /** Ensures that the value tree is updated in response to changes in the value text field */
     329    private void validate(DocumentEvent document_event)
    554330    {
    555331        try {
    556         // Edit metadata.xml files to add the metadata
    557         MetadataXMLFileManager.addMetadata(file_nodes, metadata_value);
    558 
    559         // Update the metadata value table and tree
    560         metadata_edit_event = true;
    561         metadata_value_table.rebuildAndSelectRowWithValueClosestTo(metadata_value);
    562         metadata_edit_event = false;
     332        Document document = document_event.getDocument();
     333        String metadata_value_string = document.getText(0, document.getLength());
     334        metadata_value_tree_pane.selectBestPathForMetadataValue(metadata_value_string);
    563335        }
    564336        catch (Exception exception) {
    565         // We need to catch any exceptions here so the interface is unlocked below
    566337        DebugStream.printStackTrace(exception);
    567338        }
    568 
    569         // Operation finished, so turn busy cursor off and unlock interface
    570         Gatherer.g_man.wait(false);
    571     }
    572     }
    573 
    574 
    575     private class ReplaceMetadataTask
    576     extends Thread
    577     {
    578     private MetadataValue metadata_value = null;
    579     private MetadataValueTableEntry selected_metadata_value_table_entry = null;
    580 
    581     private ReplaceMetadataTask(MetadataValue metadata_value, MetadataValueTableEntry selected_metadata_value_table_entry)
    582     {
    583         this.metadata_value = metadata_value;
    584         this.selected_metadata_value_table_entry = selected_metadata_value_table_entry;
    585     }
    586 
    587     public void run()
    588     {
    589         try {
    590         // Edit metadata.xml files to replace the metadata
    591         MetadataXMLFileManager.removeMetadata(file_nodes, selected_metadata_value_table_entry);
    592         MetadataXMLFileManager.addMetadata(file_nodes, metadata_value);
    593 
    594         // Update the metadata value table and tree
    595         metadata_edit_event = true;
    596         metadata_value_table.rebuildAndSelectRowWithValueClosestTo(metadata_value);
    597         metadata_edit_event = false;
    598         }
    599         catch (Exception exception) {
    600         // We need to catch any exceptions here so the interface is unlocked below
    601         DebugStream.printStackTrace(exception);
    602         }
    603 
    604         // Operation finished, so turn busy cursor off and unlock interface
    605         Gatherer.g_man.wait(false);
    606     }
    607     }
    608 
    609 
    610     private class RemoveMetadataTask
    611     extends Thread
    612     {
    613     private MetadataValueTableEntry selected_metadata_value_table_entry = null;
    614 
    615     private RemoveMetadataTask(MetadataValueTableEntry selected_metadata_value_table_entry)
    616     {
    617         this.selected_metadata_value_table_entry = selected_metadata_value_table_entry;
    618     }
    619 
    620     public void run()
    621     {
    622         try {
    623         // Edit metadata.xml files to remove the metadata
    624         MetadataXMLFileManager.removeMetadata(file_nodes, selected_metadata_value_table_entry);
    625 
    626         // Update the metadata value table and tree
    627         metadata_edit_event = true;
    628         metadata_value_table.rebuildAndSelectRowWithValueClosestTo(selected_metadata_value_table_entry);
    629         metadata_edit_event = false;
    630         }
    631         catch (Exception exception) {
    632         // We need to catch any exceptions here so the interface is unlocked below
    633         DebugStream.printStackTrace(exception);
    634         }
    635 
    636         // Operation finished, so turn busy cursor off and unlock interface
    637         Gatherer.g_man.wait(false);
    638     }
    639     }
    640 
    641 
    642     private class MetadataValueTable
    643     extends JTable
    644     {
    645     private MetadataValueTableModel metadata_value_table_model = null;
    646     /** The currently selected metadata value table entry */
    647     private MetadataValueTableEntry selected_metadata_value_table_entry = null;
    648 
    649 
    650     public MetadataValueTable()
    651     {
    652         metadata_value_table_model = new MetadataValueTableModel();
    653         setModel(metadata_value_table_model);
    654 
    655         // We allow only one row to be selected at a time
    656         setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    657         getSelectionModel().addListSelectionListener(new MetadataValueTableListSelectionListener());
    658 
    659         // The default JTable doesn't quite have the desired properties - when a
    660         // table cell is clicked on, the table is scrolled so the cell is as
    661         // visible as possible. This means that the left-most cells can become
    662         // hidden. To fix this, the MouseInputHandler in BasicTableUI is removed
    663         // as a MouseListener on the table, and a very slightly altered copy is
    664         // added in its place (TableMouseInputHandler).
    665         MouseListener[] mls = getMouseListeners();
    666         for (int i = 0; i < mls.length; i++) {
    667         // Remove the instances of MouseInputHandler
    668         if (mls[i].toString().startsWith("javax.swing.plaf.basic.BasicTableUI$MouseInputHandler@")) {
    669             removeMouseListener(mls[i]);
     339    }
     340    }
     341
     342
     343    private class MetadataValueTextFieldKeyListener
     344    extends KeyAdapter
     345    {
     346    /** Gives notification of key events on the text field */
     347    public void keyPressed(KeyEvent key_event)
     348    {
     349        // Tab: Auto-complete what is selected in the metadata value tree
     350        if (key_event.getKeyCode() == KeyEvent.VK_TAB) {
     351        MetadataValueTreeNode selected_metadata_value_tree_node = metadata_value_tree_pane.getSelectedMetadataValueTreeNode();
     352        if (selected_metadata_value_tree_node != null) {
     353            metadata_value_table_pane.setMetadataValueTextFieldValue(selected_metadata_value_tree_node.getFullValue());
    670354        }
    671         }
    672         // Add the slightly-altered mouse listener
    673         addMouseListener(new TableMouseInputHandler());
    674 
    675         // We also have to ensure that the table column header hasn't gone on a severe Jenny Craig binge and has somehow lost 7/8th of its component size
    676         JTableHeader table_header = getTableHeader();
    677         Dimension table_header_preferred_size = table_header.getPreferredSize();
    678         if (table_header_preferred_size.height < MINIMUM_TABLE_HEADER_SIZE) {
    679         table_header_preferred_size.setSize(table_header_preferred_size.width, MINIMUM_TABLE_HEADER_SIZE);
    680         table_header.setPreferredSize(table_header_preferred_size);
    681         }
    682 
    683         // Set the table columns as we want them
    684         setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    685         Dimension table_size = getPreferredSize();
    686         TableColumnModel column_model = getColumnModel();
    687 
    688         TableColumn inherited_column = column_model.getColumn(0);
    689         inherited_column.setPreferredWidth(20);
    690         inherited_column.setCellRenderer(new MetadataValueTableCellRenderer(metadata_value_table_model));
    691 
    692         TableColumn element_column = column_model.getColumn(1);
    693         element_column.setPreferredWidth((TABLE_SIZE.width / 4) - 15);
    694         element_column.setCellRenderer(new MetadataValueTableCellRenderer(metadata_value_table_model));
    695 
    696         TableColumn value_column = column_model.getColumn(2);
    697         value_column.setPreferredWidth(((3 * TABLE_SIZE.width) / 4) - 30);
    698         value_column.setCellRenderer(new MetadataValueTableCellRenderer(metadata_value_table_model));
    699     }
    700 
    701 
    702     public MetadataElement getSelectedMetadataElement()
    703     {
    704         return selected_metadata_value_table_entry.getMetadataElement();
    705     }
    706 
    707 
    708     public MetadataValueTableEntry getSelectedMetadataValueTableEntry()
    709     {
    710         return selected_metadata_value_table_entry;
    711     }
    712 
    713 
    714     public boolean isSelectedMetadataValueTableEntryCommon()
    715     {
    716         return metadata_value_table_model.isCommon(selected_metadata_value_table_entry);
    717     }
    718 
    719 
    720     public void moveSelectionDown()
    721     {
    722         int new_row_to_select = getSelectedRow() + 1;
    723         if (new_row_to_select < metadata_value_table_model.getRowCount()) {
    724         changeSelection(new_row_to_select, 1, false, false);
    725         }
    726     }
    727 
    728 
    729     public void moveSelectionUp()
    730     {
    731         int new_row_to_select = getSelectedRow() - 1;
    732         if (new_row_to_select >= 0) {
    733         changeSelection(new_row_to_select, 1, false, false);
    734         }
    735     }
    736 
    737 
    738     public void rebuildAndSelectRowWithValueClosestTo(MetadataValue optimal_metadata_value_to_select_when_building_complete)
    739     {
    740         // We don't want a lot of ListSelectionEvents while the table is rebuilding
    741         clearSelection();
    742 
    743         // Rebuild the metadata value table model
    744         metadata_value_table_model.rebuild(file_nodes);
    745 
    746         // If the metadata value table is empty there isn't much to do
    747         if (metadata_value_table_model.getRowCount() == 0) {
    748         // ...except display the "no metadata available" card, unless no files are selected
    749         if (file_nodes != null) {
    750             metadata_value_table_card_layout.show(table_card_pane, TABLE_CARD_NO_METADATA_AVAILABLE);
    751         }
    752         return;
    753         }
    754 
    755         // Display the card with the metadata value table
    756         metadata_value_table_card_layout.show(table_card_pane, TABLE_CARD);
    757 
    758         // If we don't need to select a row in the table after rebuilding, we're done
    759         if (optimal_metadata_value_to_select_when_building_complete == null) {
    760         return;
    761         }
    762 
    763         MetadataElement optimal_metadata_element = optimal_metadata_value_to_select_when_building_complete.getMetadataElement();
    764        
    765         // Find the row containing the closest value to the optimal value
    766         int optimal_row_to_select = 0;
    767         for (int i = 0; i < metadata_value_table_model.getRowCount(); i++) {
    768         MetadataValueTableEntry metadata_value_table_entry = metadata_value_table_model.getMetadataValueTableEntry(i);
    769         MetadataElement metadata_element = metadata_value_table_entry.getMetadataElement();
    770         if (metadata_element.equals(optimal_metadata_element)) {
    771             // This row will be the optimal row, unless the next row matches better
    772             optimal_row_to_select = i;
    773         }
    774 
    775         int c = metadata_value_table_entry.compareTo(optimal_metadata_value_to_select_when_building_complete);
    776         if (c >= 0) {
    777             // We've either found an exact match, or there is no exact match, so stop here
    778             break;
    779         }
    780         }
    781 
    782         changeSelection(optimal_row_to_select, 1, false, false);
    783         metadata_value_text_field.requestFocus();
    784     }
    785 
    786 
    787     private class MetadataValueTableListSelectionListener
    788         implements ListSelectionListener
    789     {
    790         public void valueChanged(ListSelectionEvent event)
    791         {
    792         // We only want to handle one event per selection, so wait for the value to stabilise
    793         if (getSelectionModel().getValueIsAdjusting() == true) {
    794             return;
    795         }
    796 
    797         selected_metadata_value_table_entry = null;
    798         MetadataElement selected_metadata_element = null;
    799         String selected_metadata_value = "";
    800 
    801         // We have a SINGLE_SELECTION model set so there is at most one selection
    802         int selected_row = getSelectedRow();
    803         if (selected_row >= 0) {
    804             selected_metadata_value_table_entry = metadata_value_table_model.getMetadataValueTableEntry(selected_row);
    805             selected_metadata_element = selected_metadata_value_table_entry.getMetadataElement();
    806             MetadataValueTreeNode metadata_value_tree_node = selected_metadata_value_table_entry.getMetadataValueTreeNode();
    807             if (metadata_value_tree_node != null) {
    808             selected_metadata_value = metadata_value_tree_node.getFullValue();
    809             }
    810         }
    811 
    812         // Update the metadata value tree
    813         metadata_value_tree.rebuild(selected_metadata_element, selected_metadata_value);
    814         }
    815     }
    816 
    817 
    818     /** A direct copy of javax.swing.plaf.basic.BasicTableUI$MouseInputHandler, except for one change in adjustFocusAndSelection(). The purpose of this change is to always keep the left-most cells of the table row selected visible. */
    819     private class TableMouseInputHandler implements MouseInputListener
    820     {
    821         // Component receiving mouse events during editing.
    822         // May not be editorComponent.
    823         private Component dispatchComponent;
    824         private boolean selectedOnPress;
    825 
    826         //  The Table's mouse listener methods.
    827 
    828         public void mouseClicked(MouseEvent e) {}
    829 
    830         private void setDispatchComponent(MouseEvent e) {
    831         Component editorComponent = getEditorComponent();
    832         Point p = e.getPoint();
    833         Point p2 = SwingUtilities.convertPoint(metadata_value_table, p, editorComponent);
    834         dispatchComponent = SwingUtilities.getDeepestComponentAt(editorComponent,
    835                                      p2.x, p2.y);
    836         }
    837 
    838         private boolean repostEvent(MouseEvent e) {
    839         // Check for isEditing() in case another event has
    840         // caused the editor to be removed. See bug #4306499.
    841         if (dispatchComponent == null || !isEditing()) {
    842             return false;
    843         }
    844         MouseEvent e2 = SwingUtilities.convertMouseEvent(metadata_value_table, e, dispatchComponent);
    845         dispatchComponent.dispatchEvent(e2);
    846         return true;
    847         }
    848 
    849         private void setValueIsAdjusting(boolean flag) {
    850         getSelectionModel().setValueIsAdjusting(flag);
    851         getColumnModel().getSelectionModel().setValueIsAdjusting(flag);
    852         }
    853 
    854         private boolean shouldIgnore(MouseEvent e) {
    855         return e.isConsumed() || (!(SwingUtilities.isLeftMouseButton(e) && isEnabled()));
    856         }
    857 
    858         public void mousePressed(MouseEvent e) {
    859         if (e.isConsumed()) {
    860             selectedOnPress = false;
    861             return;
    862         }
    863         selectedOnPress = true;
    864         adjustFocusAndSelection(e);
    865         }
    866 
    867         void adjustFocusAndSelection(MouseEvent e) {
    868         if (shouldIgnore(e)) {
    869             return;
    870         }
    871 
    872         Point p = e.getPoint();
    873         int row = rowAtPoint(p);
    874         int column = columnAtPoint(p);
    875         // The autoscroller can generate drag events outside the Table's range.
    876         if ((column == -1) || (row == -1)) {
    877             return;
    878         }
    879 
    880         if (editCellAt(row, column, e)) {
    881             setDispatchComponent(e);
    882             repostEvent(e);
    883         }
    884         // Commented this out and added the line below it to keep the value text field in focus
    885         // else if (isRequestFocusEnabled()) {
    886         // requestFocus();
    887         // }
    888         metadata_value_text_field.requestFocus();
    889 
    890         CellEditor editor = getCellEditor();
    891         if (editor == null || editor.shouldSelectCell(e)) {
    892             boolean adjusting = (e.getID() == MouseEvent.MOUSE_PRESSED) ? true : false;
    893             setValueIsAdjusting(adjusting);
    894 
    895             // Special code for clicking the first column (folder-level metadata)
    896             if (column == 0) {
    897             selected_metadata_value_table_entry = metadata_value_table_model.getMetadataValueTableEntry(row);
    898 
    899             // If this metadata is inherited, switch to the folder it came from
    900             if (selected_metadata_value_table_entry.isInheritedMetadata()) {
    901                 File folder_metadata_inherited_from = selected_metadata_value_table_entry.getFolderMetadataInheritedFrom();
    902                 if (folder_metadata_inherited_from != null) {
    903                 collection_tree.setSelection(folder_metadata_inherited_from);
    904                 }
    905             }
    906 
    907             changeSelection(row, 0, e.isControlDown(), e.isShiftDown());
    908             }
    909             else {
    910             changeSelection(row, 1, e.isControlDown(), e.isShiftDown());
    911             }
    912         }
    913         }
    914 
    915         public void mouseReleased(MouseEvent e) {
    916         if (selectedOnPress) {
    917             if (shouldIgnore(e)) {
    918             return;
    919             }
    920 
    921             repostEvent(e);
    922             dispatchComponent = null;
    923             setValueIsAdjusting(false);
    924         } else {
    925             adjustFocusAndSelection(e);
    926         }
    927         }
    928 
    929 
    930         public void mouseEntered(MouseEvent e) {}
    931 
    932         public void mouseExited(MouseEvent e) {}
    933 
    934         //  The Table's mouse motion listener methods.
    935 
    936         public void mouseMoved(MouseEvent e) {}
    937 
    938         public void mouseDragged(MouseEvent e) {
    939         if (shouldIgnore(e)) {
    940             return;
    941         }
    942 
    943         repostEvent(e);
    944 
    945         CellEditor editor = getCellEditor();
    946         if (editor == null || editor.shouldSelectCell(e)) {
    947             Point p = e.getPoint();
    948             int row = rowAtPoint(p);
    949             int column = columnAtPoint(p);
    950             // The autoscroller can generate drag events outside the Table's range.
    951             if ((column == -1) || (row == -1)) {
    952             return;
    953             }
    954             changeSelection(row, column, false, true);
    955         }
    956         }
    957     }
    958     }
    959 
    960 
    961     /**
    962      * This class is a little bit complex, a little bit subtle, and a little bit odd.
    963      * The strange interaction model is due to the fact that it is very tightly
    964      * coupled to the EnrichPane.
    965      *
    966      * The interaction is complex because there are three separate controls that the
    967      * user may interact with, each of which can affect the other two. The three
    968      * controls are:
    969      *   - The "assigned metadata" table, at the top right of the meta edit pane.
    970      *   - The "metadata value" text field, where users can type in new values.
    971      *   - The "metadata value tree", which shows other values that have been
    972      *     assigned to the selected metadata element.
    973      *
    974      * The interactions are:
    975      *   - The "assigned metadata" table
    976      *     Users may select one (and only one) row in this table. Selecting a row
    977      *     chooses one metadata element. The text field will be set to the current
    978      *     value of the metadata element. This value will also be selected in the
    979      *     metadata value tree.
    980      *
    981      *   - The "metadata value" text field
    982      *     If the value the user has typed in this is a prefix of an entry in the
    983      *     value tree, this value will be selected in the value tree. In this case,
    984      *     pressing "Tab" will complete the value (ie. make the value in the text
    985      *     field the same as the value in the tree). This is to allow users to
    986      *     quickly and easily assign existing metadata values to new documents.
    987      *
    988      *   - The "metadata value tree"
    989      *     Selecting a value in the tree will set the text field to this value.
    990      */
    991     private class MetadataValueTree
    992     extends JPanel
    993     {
    994     private boolean ignore_tree_selection_event = false;
    995     private boolean manual_text_edit_event = false;
    996     private CardLayout card_layout;
    997     private String card_showing = null;
    998     /** The metadata element that is currently selected. */
    999     private MetadataElement selected_metadata_element = null;
    1000     private JTextArea extracted_message;
    1001     private JTree tree;
    1002 
    1003     static final private String NONE = "None";
    1004     static final private String TREE = "Tree";
    1005 
    1006 
    1007     public MetadataValueTree(int width, int height)
    1008     {
    1009         super();
    1010 
    1011         setFont(Configuration.getFont("general.font", false));
    1012         setPreferredSize(new Dimension(width, height));
    1013 
    1014         JPanel metadata_pane = new JPanel();
    1015 
    1016         JPanel value_pane = new JPanel();
    1017         JLabel value_label = new JLabel();
    1018         Dictionary.registerText(value_label, "EnrichPane.Value");
    1019 
    1020         JPanel value_field_pane = new JPanel();
    1021         metadata_value_text_field.setBackground(Configuration.getColor("coloring.editable_background", false));
    1022         metadata_value_text_field.setForeground(Configuration.getColor("coloring.editable_foreground", false));
    1023         metadata_value_text_field.setPreferredSize(new Dimension(413, 24));
    1024         metadata_value_text_field.getDocument().addDocumentListener(new DocumentListenerImpl());
    1025         metadata_value_text_field.addKeyListener(new MetadataValueTextFieldKeyListener());
    1026         metadata_value_text_field.setFocusTraversalKeysEnabled(false);
    1027         Dictionary.setTooltip(metadata_value_text_field, "EnrichPane.Value_Field_Tooltip");
    1028 
    1029         JPanel button_pane = new JPanel();
    1030 
    1031         JPanel tree_pane = new JPanel();
    1032         JLabel tree_label = new JLabel();
    1033         Dictionary.registerText(tree_label, "EnrichPane.Tree");
    1034 
    1035         tree = new JTree();
    1036         tree.addMouseListener(new MetadataValueTreeMouseListener());
    1037         tree.addTreeSelectionListener(new MetadataValueTreeSelectionListener());
    1038         tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
    1039         tree.setRootVisible(false);
    1040         tree.putClientProperty("JTree.lineStyle", "Angled");
    1041 
    1042         JPanel extracted_pane = new JPanel();
    1043         JPanel extracted_header_pane = new JPanel();
    1044         extracted_message = new JTextArea("");
    1045         extracted_message.setEditable(false);
    1046         extracted_message.setLineWrap(true);
    1047         extracted_message.setOpaque(false);
    1048         extracted_message.setWrapStyleWord(true);
    1049 
    1050         card_layout = new CardLayout();
    1051 
    1052         // Layout
    1053         value_field_pane.setBorder(BorderFactory.createEmptyBorder(0,5,0,5));
    1054         value_field_pane.setLayout(new BorderLayout(0, 0));
    1055         value_field_pane.add(metadata_value_text_field, BorderLayout.CENTER);
    1056 
    1057         button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
    1058         button_pane.setLayout(new GridLayout());
    1059         button_pane.add(add);
    1060         button_pane.add(replace);
    1061         button_pane.add(remove);
    1062 
    1063         value_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    1064         value_pane.setLayout(new BorderLayout());
    1065         value_pane.add(value_label, BorderLayout.WEST);
    1066         value_pane.add(value_field_pane, BorderLayout.CENTER);
    1067         value_pane.add(expand, BorderLayout.EAST);
    1068         value_pane.add(button_pane, BorderLayout.SOUTH);
    1069 
    1070         tree_pane.setLayout(new BorderLayout());
    1071         tree_pane.add(tree_label, BorderLayout.NORTH);
    1072         tree_pane.add(new JScrollPane(tree), BorderLayout.CENTER);
    1073 
    1074         metadata_pane.setLayout(new BorderLayout());
    1075         metadata_pane.add(value_pane, BorderLayout.NORTH);
    1076         metadata_pane.add(tree_pane, BorderLayout.CENTER);
    1077 
    1078         extracted_header_pane.setLayout(new BorderLayout());
    1079         extracted_header_pane.add(expand_for_extracted, BorderLayout.EAST);
    1080 
    1081         extracted_pane.setBorder(BorderFactory.createEmptyBorder(0,10,25,0));
    1082         extracted_pane.setLayout(new BorderLayout());
    1083         extracted_pane.add(extracted_header_pane, BorderLayout.NORTH);
    1084         extracted_pane.add(extracted_message, BorderLayout.CENTER);
    1085 
    1086         this.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    1087         this.setLayout(card_layout);
    1088         this.add(metadata_pane, TREE);
    1089         this.add(extracted_pane, NONE);
    1090         card_showing = TREE;
    1091     }
    1092 
    1093 
    1094     /** Returns a TreePath for the node most closely matching the metadata value. */
    1095     private TreePath getClosestPath(String val)
    1096     {
    1097         // Start at the root of the tree
    1098         MetadataValueTreeNode tree_node = (MetadataValueTreeNode) tree.getModel().getRoot();
    1099 
    1100         // Separate hierarchical values
    1101         PatternTokenizer tokenizer = new PatternTokenizer(val, MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN);
    1102         while (tokenizer.hasMoreTokens()) {
    1103         String token = tokenizer.nextToken();
    1104 
    1105         // All components except the last must match exactly
    1106         if (tokenizer.hasMoreTokens()) {
    1107             for (int i = 0; i < tree_node.getChildCount(); i++) {
    1108             MetadataValueTreeNode child_node = (MetadataValueTreeNode) tree_node.getChildAt(i);
    1109             if (child_node.getValue().equals(token)) {
    1110                 // The matching node has been found, so move onto the next token
    1111                 tree_node = child_node;
    1112                 break;
    1113             }
    1114             }
    1115         }
    1116 
    1117         // The last component may match partially
    1118         else {
    1119             for (int i = 0; i < tree_node.getChildCount(); i++) {
    1120             MetadataValueTreeNode child_node = (MetadataValueTreeNode) tree_node.getChildAt(i);
    1121             if (child_node.getFullValue().startsWith(val)) {
    1122                 // The closest node has been found, so return its path
    1123                 return new TreePath(child_node.getPath());
    1124             }
    1125             }
    1126 
    1127             // Not even a partial match
    1128             return null;
    1129         }
    1130         }
    1131 
    1132         // If nothing else, return the path of the root node
    1133         return new TreePath(tree_node.getPath());
    1134     }
    1135 
    1136 
    1137     public String getSelectedValue()
    1138     {
    1139         String metadata_value_raw = metadata_value_text_field.getText();
    1140         String metadata_value = XMLTools.removeInvalidCharacters(metadata_value_raw);
    1141         return metadata_value.replaceAll("\\\\", MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN);
    1142     }
    1143 
    1144 
    1145     public void rebuild(MetadataElement metadata_element, String metadata_value)
    1146     {
    1147         // System.err.println("In MetadataValueTree.rebuild()...");
    1148 
    1149         // Clear selection
    1150         if (metadata_element == null) {
    1151         if (metadata_edit_event == false) {
    1152             // System.err.println("Clearing selection...");
    1153             selected_metadata_element = null;
    1154             validateDisplay();
    1155         }
    1156         return;
    1157         }
    1158 
    1159         // Reload the value tree model if the selected metadata element has changed
    1160         if (selected_metadata_element != metadata_element) {
    1161         selected_metadata_element = metadata_element;
    1162         tree.setModel(selected_metadata_element.getMetadataValueTreeModel());
    1163         }
    1164 
    1165         validateDisplay();
    1166 
    1167         setSelectedValue(metadata_value);
    1168     }
    1169 
    1170 
    1171     /** This function is copied from JTree::setPathToVisible(), and modified so tree rows
    1172         are always shown flushed left. Note: doesn't do accessibleContext stuff. */
    1173     private void scrollTreePathToVisible(TreePath path)
    1174     {
    1175         if (path != null) {
    1176         tree.makeVisible(path);
    1177 
    1178         Rectangle bounds = tree.getPathBounds(path);
    1179         if (bounds != null) {
    1180             bounds.width += bounds.x;
    1181             bounds.x = 0;
    1182             tree.scrollRectToVisible(bounds);
    1183         }
    1184         }
    1185     }
    1186 
    1187 
    1188     /** Sets the value in the text field. */
    1189     private void setSelectedValue(String metadata_value)
    1190     {
    1191         // Setting the text of the field causes the DocumentListener to be notified, and
    1192         //   updating the tree is handled there (DocumentListenerImpl::validate())
    1193         if (!card_showing.equals(NONE)) {
    1194         manual_text_edit_event = metadata_value.equals("");  // Set to false unless val == ""
    1195         metadata_value_text_field.setText(metadata_value);
    1196         manual_text_edit_event = true;
    1197         }
    1198     }
    1199 
    1200 
    1201     private void validateDisplay()
    1202     {
    1203         // If no metadata is selected in the metadata value table, display "no metadata element selected" card
    1204         if (file_nodes == null || selected_metadata_element == null) {
    1205         metadata_value_tree_card_layout.show(control_pane, TREE_CARD_NO_METADATA_ELEMENT_SELECTED);
    1206         }
    1207 
    1208         // Otherwise, display the card with the metadata value tree
    1209         else {
    1210         metadata_value_tree_card_layout.show(control_pane, TREE_CARD);
    1211 
    1212         // Special case the extracted metadata set
    1213         if (selected_metadata_element.getNamespace().equals(Utility.EXTRACTED_METADATA_NAMESPACE)) {
    1214             // Display the panel showing the "you cannot edit this metadata" message
    1215             String[] args = new String[1];
    1216             args[0] = selected_metadata_element.getFullName();
    1217             Dictionary.registerText(extracted_message, "EnrichPane.AutoMessage", args);
    1218             card_layout.show(this, NONE);
    1219             card_showing = NONE;
    1220         }
    1221         else {
    1222             // Display the panel for viewing and assigning metadata
    1223             card_layout.show(this, TREE);
    1224             card_showing = TREE;
    1225         }
    1226 
    1227         // Validate the buttons above the value tree
    1228         MetadataValueTableEntry metadata_value_table_entry = metadata_value_table.getSelectedMetadataValueTableEntry();
    1229         if (metadata_value_table_entry.getValue().equals("")) {
    1230             // Can only append if something has been entered
    1231             add.setEnabled((getSelectedValue().length() > 0));
    1232 
    1233             // Nothing to replace or remove
    1234             replace.setEnabled(false);
    1235             remove.setEnabled(false);
    1236             return;
    1237         }
    1238 
    1239         // Check if the text in the value field is the same as the metadata value
    1240         if (getSelectedValue().equals(metadata_value_table_entry.getFullValue())) {
    1241             // Can't replace
    1242             replace.setEnabled(false);
    1243 
    1244             // Adding, however, is dependant on whether the selected metadata is common or uncommon. If the later then you can append so as to make it common.
    1245             add.setEnabled(!metadata_value_table.isSelectedMetadataValueTableEntryCommon());
    1246         }
    1247         else {
    1248             // Can append or replace, if something has been entered
    1249             add.setEnabled((getSelectedValue().length() > 0));
    1250             replace.setEnabled((getSelectedValue().length() > 0));
    1251         }
    1252 
    1253         // Can only remove if the metadata is file level
    1254         if (metadata_value_table_entry != null) {  // Shouldn't be necessary, but is
    1255             remove.setEnabled((metadata_value_table_entry.isInheritedMetadata() == false));
    1256         }
    1257         }
    1258     }
    1259 
    1260 
    1261     private class MetadataValueTextFieldKeyListener
    1262         extends KeyAdapter
    1263     {
    1264         /** Gives notification of key events on the text field */
    1265         public void keyPressed(KeyEvent e)
    1266         {
    1267         // Tab: Auto-complete
    1268         if (e.getKeyCode() == KeyEvent.VK_TAB) {
    1269             if (tree.getSelectionCount() != 0 && !getSelectedValue().equals("")) {
    1270             TreePath path = tree.getSelectionPath();
    1271             MetadataValueTreeNode node = (MetadataValueTreeNode) path.getLastPathComponent();
    1272             setSelectedValue(node.getFullValue());
    1273             }
    1274         }
    1275 
    1276         // Enter: Append the metadata value, if we're allowed to
    1277         if (e.getKeyCode() == KeyEvent.VK_ENTER) {
    1278             if (add.isEnabled()) {
    1279             add.doClick();
    1280             }
    1281         }
    1282 
    1283         // Down: Change the metadata value table selection
    1284         if (e.getKeyCode() == KeyEvent.VK_DOWN) {
    1285             metadata_value_table.moveSelectionDown();
    1286         }
    1287 
    1288         // Up: Change the metadata value table selection
    1289         if (e.getKeyCode() == KeyEvent.VK_UP) {
    1290             metadata_value_table.moveSelectionUp();
    1291         }
    1292         }
    1293     }
    1294 
    1295 
    1296     private class MetadataValueTreeMouseListener
    1297         extends MouseAdapter
    1298     {
    1299         public void mouseClicked(MouseEvent e)
    1300         {
    1301         // Double click: assign the selected value tree node immediately (if possible)
    1302         if (e.getClickCount() == 2 && tree.getSelectionCount() != 0) {
    1303             TreePath path = tree.getSelectionPath();
    1304             MetadataValueTreeNode node = (MetadataValueTreeNode) path.getLastPathComponent();
    1305 
    1306             // Leaf nodes only (folder nodes will expand/contract)
    1307             if (node.getChildCount() == 0 && add.isEnabled()) {
    1308             add.doClick();
    1309             }
    1310         }
    1311         }
    1312     }
    1313 
    1314 
    1315     private class MetadataValueTreeSelectionListener
    1316         implements TreeSelectionListener
    1317     {
    1318         public void valueChanged(TreeSelectionEvent event)
    1319         {
    1320         // Select a node in the tree: fill the metadata value text field with the selected node's value
    1321         if (tree.getSelectionCount() != 0 && !ignore_tree_selection_event) {
    1322             TreePath path = tree.getSelectionPath();
    1323             MetadataValueTreeNode node = (MetadataValueTreeNode) path.getLastPathComponent();
    1324             setSelectedValue(node.getFullValue());
    1325         }
    1326         }
    1327     }
    1328 
    1329 
    1330     private class DocumentListenerImpl
    1331         implements DocumentListener
    1332     {
    1333         /** Gives notification that an attribute or set of attributes changed */
    1334         public void changedUpdate(DocumentEvent e) {
    1335         validate();
    1336         }
    1337 
    1338         /** Gives notification that there was an insert into the document */
    1339         public void insertUpdate(DocumentEvent e) {
    1340         validate();
    1341         }
    1342 
    1343         /** Gives notification that a portion of the document has been removed */
    1344         public void removeUpdate(DocumentEvent e) {
    1345         validate();
    1346         }
    1347 
    1348 
    1349         /** Ensures that the value text field and value tree are synchronized */
    1350         private void validate()
    1351         {
    1352         String value_text = getSelectedValue();
    1353 
    1354         // Ignore the validate() with empty text that occurs when value.setText() is used
    1355         if (!value_text.equals("") || manual_text_edit_event == true) {
    1356             TreePath closest_path = getClosestPath(value_text);
    1357 
    1358             // Select the new path in the tree
    1359             // The tree selection event this causes must be ignored, since it alters value
    1360             ignore_tree_selection_event = true;
    1361             tree.setSelectionPath(closest_path);
    1362             ignore_tree_selection_event = false;
    1363 
    1364             // If a folder has been specified, make sure it is expanded
    1365             if (value_text.endsWith(MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN) && !tree.isExpanded(closest_path)) {
    1366             tree.expandPath(closest_path);
    1367             }
    1368 
    1369             // Make sure the path is visible on the screen
    1370             scrollTreePathToVisible(closest_path);
    1371             // One call should be enough, but it isn't in some cases (for some reason)
    1372             scrollTreePathToVisible(closest_path);
    1373 
    1374             // Update the status of the buttons
    1375             validateDisplay();
    1376         }
     355
     356        // We do not want this event to be processed by the table also
     357        key_event.consume();
     358        }
     359
     360        // Enter: save the current value then add a blank row for the selected metadata element
     361        if (key_event.getKeyCode() == KeyEvent.VK_ENTER) {
     362        metadata_value_table_pane.stopEditingAndAddBlankRowForSelectedMetadataElement();
     363        }
     364    }
     365    }
     366
     367
     368    private class MetadataValueTreeSelectionListener
     369    implements TreeSelectionListener
     370    {
     371    public void valueChanged(TreeSelectionEvent tree_selection_event)
     372    {
     373        // When a node is selected in the tree, fill the metadata value text field with the selected node's value
     374        MetadataValueTreeNode selected_metadata_value_tree_node = metadata_value_tree_pane.getSelectedMetadataValueTreeNode();
     375        if (selected_metadata_value_tree_node != null) {
     376        metadata_value_table_pane.setMetadataValueTextFieldValue(selected_metadata_value_tree_node.getFullValue());
    1377377        }
    1378378    }
     
    1538538        }
    1539539        }
    1540         // finally we have the correct selection paths!!
     540        // finally we have the correct selection paths!
    1541541
    1542542        // Create an appropriate context menu, based on what is selected
  • trunk/gli/src/org/greenstone/gatherer/metadata/MetadataTools.java

    r8996 r9856  
    119119    return "";
    120120    }
     121
     122
     123    static public String getRegularExpressionThatMatchesFilePath(String file_path)
     124    {
     125    // Convert the file path into a regular expression that will match it
     126    String file_path_regexp = file_path;
     127    file_path_regexp = file_path_regexp.replaceAll("\\.", "\\\\.");
     128    file_path_regexp = file_path_regexp.replaceAll("\\(", "\\\\(");
     129    file_path_regexp = file_path_regexp.replaceAll("\\)", "\\\\)");
     130    file_path_regexp = file_path_regexp.replaceAll("\\[", "\\\\[");
     131    file_path_regexp = file_path_regexp.replaceAll("\\]", "\\\\]");
     132    file_path_regexp = file_path_regexp.replaceAll("\\{", "\\\\{");
     133    file_path_regexp = file_path_regexp.replaceAll("\\}", "\\\\}");
     134    file_path_regexp = file_path_regexp.replaceAll("\\+", "\\\\+");
     135    return file_path_regexp;
     136    }
    121137}
  • trunk/gli/src/org/greenstone/gatherer/metadata/MetadataValueTableEntry.java

    r9846 r9856  
    2828
    2929
    30 /** This class represents one metadata (element, value) pair */
     30/** This class represents one entry in the metadata value table. */
    3131public class MetadataValueTableEntry
    3232    extends MetadataValue
  • trunk/gli/src/org/greenstone/gatherer/metadata/MetadataValueTableModel.java

    r9350 r9856  
    3434import javax.swing.table.*;
    3535import org.greenstone.gatherer.Configuration;
     36import org.greenstone.gatherer.DebugStream;
    3637import org.greenstone.gatherer.Dictionary;
    3738import org.greenstone.gatherer.Gatherer;
     
    5152
    5253
     54    public int addBlankRowForMetadataElement(MetadataElement metadata_element)
     55    {
     56    MetadataValueTableModelBuilder metadata_value_table_model_builder = new MetadataValueTableModelBuilder();
     57    MetadataValue blank_metadata_value = new MetadataValue(metadata_element, new MetadataValueTreeNode(""));
     58    return metadata_value_table_model_builder.insertMetadataValue(blank_metadata_value);
     59    }
     60
     61
     62    public int findMetadataValueTableEntryToSelect(MetadataValueTableEntry metadata_value_table_entry)
     63    {
     64    MetadataElement metadata_element = metadata_value_table_entry.getMetadataElement();
     65
     66    // Find the correct entry to select
     67    for (int i = 0; i < metadata_value_table_entries.size(); i++) {
     68        MetadataValueTableEntry current_metadata_value_table_entry = (MetadataValueTableEntry) metadata_value_table_entries.get(i);
     69        int element_comparison = MetadataSetManager.compareMetadataElements(current_metadata_value_table_entry.getMetadataElement(), metadata_element);
     70
     71        // We've found the right element, so check if the value already exists
     72        if (element_comparison == 0) {
     73        int value_comparison = current_metadata_value_table_entry.compareTo(metadata_value_table_entry);
     74        if (value_comparison == 0) {
     75            // Entry found!
     76            return i;
     77        }
     78        }
     79
     80        // We've just gone past the correct entry to select
     81        if (element_comparison > 0) {
     82        return i - 1;
     83        }
     84    }
     85
     86    // Have to select the last entry
     87    return metadata_value_table_entries.size() - 1;
     88    }
     89
     90
     91    public Class getColumnClass(int col)
     92    {
     93    return getValueAt(0, col).getClass();
     94    }
     95
     96
    5397    /** Returns the number of columns in this table. */
    5498    public int getColumnCount()
     
    108152
    109153
    110     /** Determine if the given metadata is common to all selected file nodes given the context of the current view. */
     154    public boolean isCellEditable(int row, int col)
     155    {
     156    // The inherited and element columns are never editable
     157    if (col < 2) {
     158        return false;
     159    }
     160
     161    // Extracted and inherited metadata is not editable
     162    MetadataValueTableEntry metadata_value_table_entry = (MetadataValueTableEntry) metadata_value_table_entries.get(row);
     163    if (metadata_value_table_entry.getMetadataElement().isExtractedMetadataElement() || metadata_value_table_entry.isInheritedMetadata()) {
     164        return false;
     165    }
     166
     167    return true;
     168    }
     169
     170
     171    /** Determine if the given metadata is common to all selected file nodes. */
    111172    public boolean isCommon(MetadataValueTableEntry metadata_value_table_entry)
    112173    {
     
    115176
    116177
    117     /** Determine if the given metadata is common to all selected file nodes given the context of the current view. */
     178    /** Determine if the given metadata is common to all selected file nodes. */
    118179    public boolean isCommon(int row)
    119180    {
     
    146207
    147208
     209    public void setValueAt(Object new_metadata_value, int row, int col)
     210    {
     211    MetadataValueTableEntry metadata_value_table_entry = getMetadataValueTableEntry(row);
     212
     213    // If nothing has changed no action is necessary
     214    String old_metadata_value = metadata_value_table_entry.getFullValue();
     215    if (new_metadata_value.equals(old_metadata_value)) {
     216        return;
     217    }
     218
     219    // Lock the interface so nothing can be changed while the metadata edit is being processed
     220    Gatherer.g_man.wait(true);
     221
     222    // Metadata value added
     223    if (old_metadata_value.equals("") && !new_metadata_value.equals("")) {
     224        // If we're adding metadata to folders display the warning
     225        if (!file_nodes[0].isLeaf()) {
     226        WarningDialog dialog = new WarningDialog("warning.DirectoryLevelMetadata", "DirectoryLevelMetadata.Title", "DirectoryLevelMetadata.Message", null, true);
     227        int dialog_result = dialog.display();
     228        dialog.dispose();
     229        if (dialog_result != JOptionPane.OK_OPTION) {
     230            return;
     231        }
     232        }
     233
     234        MetadataElement metadata_element = metadata_value_table_entry.getMetadataElement();
     235        MetadataValueTreeNode metadata_value_tree_node = metadata_element.addMetadataValue((String) new_metadata_value);
     236        MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
     237        metadata_value.setIsAccumulatingMetadata(true);
     238        (new AppendMetadataTask(metadata_value)).run();
     239    }
     240
     241    // Metadata value removed
     242    else if (!old_metadata_value.equals("") && new_metadata_value.equals("")) {
     243        (new RemoveMetadataTask(metadata_value_table_entry)).run();
     244    }
     245
     246    // Metadata value replaced
     247    else {
     248        MetadataElement metadata_element = metadata_value_table_entry.getMetadataElement();
     249        MetadataValueTreeNode metadata_value_tree_node = metadata_element.addMetadataValue((String) new_metadata_value);
     250        MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
     251        metadata_value.setIsAccumulatingMetadata(!metadata_value_table_entry.isInheritedMetadata());
     252        (new ReplaceMetadataTask(metadata_value_table_entry, metadata_value)).run();
     253    }
     254    }
     255
     256
     257    private class AppendMetadataTask
     258    // extends Thread
     259    {
     260    private MetadataValue metadata_value = null;
     261
     262    private AppendMetadataTask(MetadataValue metadata_value)
     263    {
     264        this.metadata_value = metadata_value;
     265    }
     266
     267    public void run()
     268    {
     269        try {
     270        // Edit metadata.xml files to add the metadata
     271        MetadataXMLFileManager.addMetadata(file_nodes, metadata_value);
     272        }
     273        catch (Exception exception) {
     274        // We need to catch any exceptions here so the interface is unlocked below
     275        DebugStream.printStackTrace(exception);
     276        }
     277
     278        // Operation finished, so turn busy cursor off and unlock interface
     279        Gatherer.g_man.wait(false);
     280    }
     281    }
     282
     283
     284    private class ReplaceMetadataTask
     285    // extends Thread
     286    {
     287    private MetadataValueTableEntry selected_metadata_value_table_entry = null;
     288    private MetadataValue metadata_value = null;
     289
     290    private ReplaceMetadataTask(MetadataValueTableEntry selected_metadata_value_table_entry, MetadataValue metadata_value)
     291    {
     292        this.selected_metadata_value_table_entry = selected_metadata_value_table_entry;
     293        this.metadata_value = metadata_value;
     294    }
     295
     296    public void run()
     297    {
     298        try {
     299        // Edit metadata.xml files to replace the metadata
     300        MetadataXMLFileManager.replaceMetadata(file_nodes, selected_metadata_value_table_entry, metadata_value);
     301        }
     302        catch (Exception exception) {
     303        // We need to catch any exceptions here so the interface is unlocked below
     304        DebugStream.printStackTrace(exception);
     305        }
     306
     307        // Operation finished, so turn busy cursor off and unlock interface
     308        Gatherer.g_man.wait(false);
     309    }
     310    }
     311
     312
     313    private class RemoveMetadataTask
     314    // extends Thread
     315    {
     316    private MetadataValueTableEntry selected_metadata_value_table_entry = null;
     317
     318    private RemoveMetadataTask(MetadataValueTableEntry selected_metadata_value_table_entry)
     319    {
     320        this.selected_metadata_value_table_entry = selected_metadata_value_table_entry;
     321    }
     322
     323    public void run()
     324    {
     325        try {
     326        // Edit metadata.xml files to remove the metadata
     327        MetadataXMLFileManager.removeMetadata(file_nodes, selected_metadata_value_table_entry);
     328        }
     329        catch (Exception exception) {
     330        // We need to catch any exceptions here so the interface is unlocked below
     331        DebugStream.printStackTrace(exception);
     332        }
     333
     334        // Operation finished, so turn busy cursor off and unlock interface
     335        Gatherer.g_man.wait(false);
     336    }
     337    }
     338
     339
    148340    private class MetadataValueTableModelBuilder
    149341    {
    150342    public void run()
    151343    {
     344        // System.err.println("Building MetadataValueTableModel...");
     345
    152346        // Build a list of MetadataValueTableEntries that represent the metadata asssigned to the selected files
    153347        boolean hid_extracted_metadata = false;
    154         boolean has_inherited_metadata = false;
    155348        ArrayList metadata_elements_seen = new ArrayList();
    156349
     
    165358            MetadataElement metadata_element = metadata_value.getMetadataElement();
    166359
    167             // Note if there is inherited (folder-level) metadata in the table
    168             has_inherited_metadata = has_inherited_metadata || metadata_value.isInheritedMetadata();
    169 
    170360            // Insert this metadata value into the table, unless it already exists (in which case increment its count)
    171             insertMetadataValueIntoTable(metadata_value);
     361            insertMetadataValue(metadata_value);
    172362
    173363            // Remember we have seen this metadata element
     
    184374
    185375            // Insert this metadata value into the table, unless it already exists (in which case increment its count)
    186             insertMetadataValueIntoTable(metadata_value);
     376            insertMetadataValue(metadata_value);
    187377            }
    188378        }
     
    202392
    203393            // Add it to the table
    204             insertMetadataValueIntoTable(metadata_value_table_entry);
     394            insertMetadataValueTableEntry(metadata_value_table_entry);
    205395        }
    206396        }
     
    210400        showExtractedMetadataWarning();
    211401        }
    212 
    213         // If there is inherited metadata, display the warning
    214         if (has_inherited_metadata) {
    215         showInheritedMetadataWarning();
    216         }
    217     }
    218 
    219 
    220     /** Alphabetically inserts the new metadata value into the table */
    221     private void insertMetadataValueIntoTable(MetadataValue metadata_value)
    222     {
    223         insertMetadataValueIntoTable(new MetadataValueTableEntry(metadata_value));
    224     }
    225 
    226 
    227     /** Alphabetically inserts the new metadata value into the table */
    228     private void insertMetadataValueIntoTable(MetadataValueTableEntry metadata_value_table_entry)
    229     {
     402    }
     403
     404
     405    /** Inserts the new metadata value into the table */
     406    private int insertMetadataValue(MetadataValue metadata_value)
     407    {
     408        return insertMetadataValueTableEntry(new MetadataValueTableEntry(metadata_value));
     409    }
     410
     411
     412    /** Inserts the new metadata value table entry into the table */
     413    private int insertMetadataValueTableEntry(MetadataValueTableEntry metadata_value_table_entry)
     414    {
     415        MetadataElement metadata_element = metadata_value_table_entry.getMetadataElement();
     416
     417        // Find the correct place to insert the table entry
    230418        for (int i = 0; i < metadata_value_table_entries.size(); i++) {
    231419        MetadataValueTableEntry current_metadata_value_table_entry = (MetadataValueTableEntry) metadata_value_table_entries.get(i);
    232         int c = current_metadata_value_table_entry.compareTo(metadata_value_table_entry);
    233 
    234         // Insert value before existing entry
    235         if (c > 0) {
     420        int element_comparison = MetadataSetManager.compareMetadataElements(current_metadata_value_table_entry.getMetadataElement(), metadata_element);
     421
     422        // We've found the right element, so check if the value already exists
     423        if (element_comparison == 0) {
     424            int value_comparison = current_metadata_value_table_entry.compareTo(metadata_value_table_entry);
     425            if (value_comparison == 0) {
     426            // Entry already exists, so increment count (except for blank entries)
     427            if (!metadata_value_table_entry.getFullValue().equals("")) {
     428                current_metadata_value_table_entry.anotherOccurrence();
     429            }
     430            return i;
     431            }
     432        }
     433
     434        // Found insertion point
     435        if (element_comparison > 0) {
    236436            metadata_value_table_entries.add(i, metadata_value_table_entry);
    237437            fireTableRowsInserted(i, i);
    238             return;
    239         }
    240 
    241         // Entry already exists (increment existing count)
    242         if (c == 0) {
    243             current_metadata_value_table_entry.anotherOccurrence();
    244             return;
     438            return i;
    245439        }
    246440        }
     
    249443        metadata_value_table_entries.add(metadata_value_table_entry);
    250444        fireTableRowsInserted(metadata_value_table_entries.size() - 1, metadata_value_table_entries.size() - 1);
     445        return metadata_value_table_entries.size() - 1;
    251446    }
    252447
     
    264459        SwingUtilities.invokeLater(task);
    265460    }
    266 
    267 
    268     private void showInheritedMetadataWarning()
    269     {
    270         Runnable task = new Runnable() {
    271             public void run() {
    272             WarningDialog dialog = new WarningDialog("warning.InheritedMetadata", "InheritedMetadata.Title", "InheritedMetadata.Message", null, false);
    273             dialog.display();
    274             dialog.dispose();
    275             dialog = null;
    276             }
    277         };
    278         SwingUtilities.invokeLater(task);
    279     }
    280461    }
    281462}
  • trunk/gli/src/org/greenstone/gatherer/metadata/MetadataXMLFile.java

    r9525 r9856  
    6969
    7070    // Form a regular expression that specifies the scope of the metadata
    71     String file_path_regexp = file_relative_path;
    72     if (file_path_regexp.equals("")) {
     71    String file_path_regexp;
     72    if (file_relative_path.equals("")) {
     73        // Special case for matching all files in the directory
    7374        file_path_regexp = DIRECTORY_FILENAME;
    7475    }
    7576    else {
    7677        // Convert the file path into a regular expression that will match it
    77         file_path_regexp = file_path_regexp.replaceAll("\\.", "\\\\.");
    78         file_path_regexp = file_path_regexp.replaceAll("\\(", "\\\\(");
    79         file_path_regexp = file_path_regexp.replaceAll("\\)", "\\\\)");
    80         file_path_regexp = file_path_regexp.replaceAll("\\[", "\\\\[");
    81         file_path_regexp = file_path_regexp.replaceAll("\\]", "\\\\]");
    82         file_path_regexp = file_path_regexp.replaceAll("\\{", "\\\\{");
    83         file_path_regexp = file_path_regexp.replaceAll("\\}", "\\\\}");
    84         file_path_regexp = file_path_regexp.replaceAll("\\+", "\\\\+");
    85     }
     78        file_path_regexp = MetadataTools.getRegularExpressionThatMatchesFilePath(file_relative_path);
     79    }
     80
     81    // Remove any characters that are invalid in XML
     82    String metadata_value_string = XMLTools.removeInvalidCharacters(metadata_value.getFullValue());
    8683
    8784    // Square brackets need to be escaped because they are a special character in Greenstone
    88     String metadata_value_string = metadata_value.getFullValue();
    8985    metadata_value_string = metadata_value_string.replaceAll("\\[", "&#091;");
    9086    metadata_value_string = metadata_value_string.replaceAll("\\]", "&#093;");
     
    313309
    314310    // Form a regular expression that specifies the scope of the metadata
    315     String file_path_regexp = file_relative_path;
    316     if (file_path_regexp.equals("")) {
     311    String file_path_regexp;
     312    if (file_relative_path.equals("")) {
     313        // Special case for matching all files in the directory
    317314        file_path_regexp = DIRECTORY_FILENAME;
    318315    }
    319316    else {
    320317        // Convert the file path into a regular expression that will match it
    321         file_path_regexp = file_path_regexp.replaceAll("\\.", "\\\\.");
    322         file_path_regexp = file_path_regexp.replaceAll("\\(", "\\\\(");
    323         file_path_regexp = file_path_regexp.replaceAll("\\)", "\\\\)");
    324         file_path_regexp = file_path_regexp.replaceAll("\\[", "\\\\[");
    325         file_path_regexp = file_path_regexp.replaceAll("\\]", "\\\\]");
    326         file_path_regexp = file_path_regexp.replaceAll("\\{", "\\\\{");
    327         file_path_regexp = file_path_regexp.replaceAll("\\}", "\\\\}");
    328         file_path_regexp = file_path_regexp.replaceAll("\\+", "\\\\+");
    329     }
     318        file_path_regexp = MetadataTools.getRegularExpressionThatMatchesFilePath(file_relative_path);
     319    }
     320
     321    // Remove any characters that are invalid in XML
     322    String metadata_value_string = XMLTools.removeInvalidCharacters(metadata_value.getFullValue());
    330323
    331324    // Square brackets need to be escaped because they are a special character in Greenstone
    332     String metadata_value_string = metadata_value.getFullValue();
    333325    metadata_value_string = metadata_value_string.replaceAll("\\[", "&#091;");
    334326    metadata_value_string = metadata_value_string.replaceAll("\\]", "&#093;");
    335    
     327
    336328    // Read all the FileSet elements in the file
    337329    NodeList fileset_elements_nodelist = document.getElementsByTagName(FILESET_ELEMENT);
     
    378370        // Remove this Metadata element
    379371        current_metadata_element.getParentNode().removeChild(current_metadata_element);
     372        }
     373    }
     374
     375    // Rewrite the metadata.xml file
     376    XMLTools.writeXMLFile(this, document);
     377    }
     378
     379
     380    public void replaceMetadata(File file, MetadataValue old_metadata_value, MetadataValue new_metadata_value)
     381    {
     382    // Parse the metadata.xml file
     383    Document document = XMLTools.parseXMLFile(this);
     384    if (document == null) {
     385        System.err.println("Error: Could not parse metadata.xml file " + getAbsolutePath());
     386        return;
     387    }
     388
     389    // Determine the file's path relative to the location of the metadata.xml file
     390    File metadata_xml_file_directory = getParentFile();
     391    String file_relative_path = file.getAbsolutePath().substring(metadata_xml_file_directory.getAbsolutePath().length());
     392    if (file_relative_path.startsWith(File.separator)) {
     393        file_relative_path = file_relative_path.substring(File.separator.length());
     394    }
     395
     396    // Form a regular expression that specifies the scope of the metadata
     397    String file_path_regexp;
     398    if (file_relative_path.equals("")) {
     399        // Special case for matching all files in the directory
     400        file_path_regexp = DIRECTORY_FILENAME;
     401    }
     402    else {
     403        // Convert the file path into a regular expression that will match it
     404        file_path_regexp = MetadataTools.getRegularExpressionThatMatchesFilePath(file_relative_path);
     405    }
     406
     407    // Remove any characters that are invalid in XML
     408    String old_metadata_value_string = XMLTools.removeInvalidCharacters(old_metadata_value.getFullValue());
     409    String new_metadata_value_string = XMLTools.removeInvalidCharacters(new_metadata_value.getFullValue());
     410
     411    // Square brackets need to be escaped because they are a special character in Greenstone
     412    old_metadata_value_string = old_metadata_value_string.replaceAll("\\[", "&#091;");
     413    old_metadata_value_string = old_metadata_value_string.replaceAll("\\]", "&#093;");
     414    new_metadata_value_string = new_metadata_value_string.replaceAll("\\[", "&#091;");
     415    new_metadata_value_string = new_metadata_value_string.replaceAll("\\]", "&#093;");
     416
     417    // Read all the FileSet elements in the file
     418    NodeList fileset_elements_nodelist = document.getElementsByTagName(FILESET_ELEMENT);
     419    for (int i = 0; i < fileset_elements_nodelist.getLength(); i++) {
     420        Element current_fileset_element = (Element) fileset_elements_nodelist.item(i);
     421        boolean current_fileset_matches = false;
     422
     423        // Check the FileName elements of the FileSet to see if we have a match
     424        NodeList filename_elements_nodelist = current_fileset_element.getElementsByTagName(FILENAME_ELEMENT);
     425        for (int j = 0; j < filename_elements_nodelist.getLength(); j++) {
     426        Element current_filename_element = (Element) filename_elements_nodelist.item(j);
     427        String current_filename_element_value = XMLTools.getElementTextValue(current_filename_element);
     428
     429        // Only exact matches can be edited
     430        if (current_filename_element_value.equals(file_path_regexp)) {
     431            current_fileset_matches = true;
     432            break;
     433        }
     434        }
     435
     436        // The FileSet doesn't apply, so move onto the next one
     437        if (current_fileset_matches == false) {
     438        continue;
     439        }
     440
     441        // Each metadata value is only allowed to be assigned once
     442        boolean new_metadata_value_already_exists = false;
     443        Element metadata_element_to_edit = null;
     444
     445        // Find the Metadata element to replace in the fileset
     446        String metadata_element_name_full = old_metadata_value.getMetadataElement().getFullName();
     447        NodeList metadata_elements_nodelist = current_fileset_element.getElementsByTagName(METADATA_ELEMENT);
     448        for (int k = 0; k < metadata_elements_nodelist.getLength(); k++) {
     449        Element current_metadata_element = (Element) metadata_elements_nodelist.item(k);
     450
     451        // Check the metadata element name matches
     452        String current_metadata_element_name_full = current_metadata_element.getAttribute("name");
     453        if (!current_metadata_element_name_full.equals(metadata_element_name_full)) {
     454            continue;
     455        }
     456
     457        // Check the new metadata value doesn't already exist
     458        String current_metadata_value_string = XMLTools.getElementTextValue(current_metadata_element);
     459        if (current_metadata_value_string.equals(new_metadata_value_string)) {
     460            new_metadata_value_already_exists = true;
     461        }
     462
     463        // Check the metadata element value matches
     464        if (current_metadata_value_string.equals(old_metadata_value_string)) {
     465            metadata_element_to_edit = current_metadata_element;
     466        }
     467        }
     468
     469        // If the new metadata value already existed, remove the original value
     470        if (new_metadata_value_already_exists) {
     471        metadata_element_to_edit.getParentNode().removeChild(metadata_element_to_edit);
     472        }
     473        // Otherwise replace the old value with the new value
     474        else {
     475        XMLTools.setElementTextValue(metadata_element_to_edit, new_metadata_value_string);
    380476        }
    381477    }
  • trunk/gli/src/org/greenstone/gatherer/metadata/MetadataXMLFileManager.java

    r8783 r9856  
    77 * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
    88 *
    9  * Copyright (C) 2004 New Zealand Digital Library Project
     9 * Copyright (C) 2005 New Zealand Digital Library Project
    1010 *
    1111 * This program is free software; you can redistribute it and/or modify
     
    4040{
    4141    static private ArrayList metadata_xml_files = new ArrayList();
     42    /** The objects listening for MetadataChanged events. */
     43    static private ArrayList metadata_changed_listeners = new ArrayList();
    4244
    4345
     
    8385        }
    8486    }
     87
     88    // Let any listeners know that the metadata has changed
     89    fireMetadataChangedEvent(file_nodes);
     90    }
     91
     92
     93    static public void addMetadataChangedListener(MetadataChangedListener metadata_changed_listener)
     94    {
     95    metadata_changed_listeners.add(metadata_changed_listener);
    8596    }
    8697
     
    89100    {
    90101    metadata_xml_files.clear();
     102    }
     103
     104
     105    static private void fireMetadataChangedEvent(CollectionTreeNode[] file_nodes)
     106    {
     107    // Send the event off to all the MetadataChangedListeners
     108    for (int i = 0; i < metadata_changed_listeners.size(); i++) {
     109        ((MetadataChangedListener) metadata_changed_listeners.get(i)).metadataChanged(file_nodes);
     110    }
    91111    }
    92112
     
    254274        }
    255275    }
     276
     277    // Let any listeners know that the metadata has changed
     278    fireMetadataChangedEvent(file_nodes);
     279    }
     280
     281
     282    static public void removeMetadataChangedListener(MetadataChangedListener metadata_changed_listener)
     283    {
     284    metadata_changed_listeners.remove(metadata_changed_listener);
     285    }
     286
     287
     288    static public void replaceMetadata(CollectionTreeNode[] file_nodes, MetadataValue old_metadata_value, MetadataValue new_metadata_value)
     289    {
     290    // Replace the metadata in each file node in turn
     291    for (int i = 0; i < file_nodes.length; i++) {
     292        File current_file = file_nodes[i].getFile();
     293        DebugStream.println("Replacing metadata in " + current_file.getAbsolutePath());
     294
     295        // Find which metadata.xml file needs editing
     296        File current_file_directory = (current_file.isDirectory() ? current_file : current_file.getParentFile());
     297        for (int j = 0; j < metadata_xml_files.size(); j++) {
     298        MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(j);
     299
     300        // This metadata.xml file is only applicable if it is at the same level as the file
     301        if (current_file_directory.getAbsolutePath().equals(metadata_xml_file.getParentFile().getAbsolutePath())) {
     302            metadata_xml_file.replaceMetadata(current_file, old_metadata_value, new_metadata_value);
     303        }
     304        }
     305    }
     306
     307    // Let any listeners know that the metadata has changed
     308    fireMetadataChangedEvent(file_nodes);
    256309    }
    257310
  • trunk/gli/src/org/greenstone/gatherer/util/XMLTools.java

    r9160 r9856  
    178178
    179179    return new String(safe_characters, 0, j);
     180    }
     181
     182
     183    static public void setElementTextValue(Element element, String text)
     184    {
     185    // Remove all text node children
     186    NodeList children_nodelist = element.getChildNodes();
     187    for (int i = children_nodelist.getLength() - 1; i >= 0; i--) {
     188        Node child_node = children_nodelist.item(i);
     189        if (child_node.getNodeType() == Node.TEXT_NODE) {
     190        element.removeChild(child_node);
     191        }
     192    }
     193
     194    // Add a new text node
     195    if (text != null) {
     196        element.appendChild(element.getOwnerDocument().createTextNode(text));
     197    }
    180198    }
    181199
Note: See TracChangeset for help on using the changeset viewer.