Changeset 4365


Ignore:
Timestamp:
2003-05-27T15:49:22+12:00 (21 years ago)
Author:
mdewsnip
Message:

Fixed tabbing.

Location:
trunk/gli/src/org/greenstone/gatherer
Files:
31 edited

Legend:

Unmodified
Added
Removed
  • trunk/gli/src/org/greenstone/gatherer/help/ContentModel.java

    r4293 r4365  
    3838 */
    3939public class ContentModel
    40     extends DefaultTreeModel {
    41     /** Constructor that builds the contents model.
    42       * @see org.greenstone.gatherer.help.HelpItem
    43       */
    44     public ContentModel() {
    45           super(new HelpItem("Contents"));
    46           // Load the contents file and build the contents structure found within.
    47           try {
    48                 FileReader file_reader = new FileReader(Utility.HELP_DIR + "index.txt");
    49                 BufferedReader buffered_reader = new BufferedReader(file_reader);
    50                 String line = null;
    51                 HashMap known_entries = new HashMap();
    52                 while((line = buffered_reader.readLine()) != null) {
    53                      // We'll use my ever popular command tokenizer to ensure we can parse index | title.
    54                      CommandTokenizer tokenizer = new CommandTokenizer(line);
    55                      // The first thing off the tokenizer is an index of the form xx.xx.xx
    56                      String index_str = tokenizer.nextToken();
    57                      // And the second is a title for this entry
    58                      String title = tokenizer.nextToken();
    59                      // Now we determine the parent of this entry. In general this involves removing the last portion of the index so 2.3 becomes 2. However there is a special case when the index has only one part, ie 2, in which case it get added to the root, then the index is replaced with 2.0.
    60                      int position = -1;
    61                      if((position = index_str.lastIndexOf(".")) != -1) {
    62                           // Find parent index
    63                           String parent_index = index_str.substring(0, position);
    64                           // Retrieve parent item
    65                           HelpItem parent_item = (HelpItem) known_entries.get(parent_index);
    66                           // Otherwise append ".0" and search again...
    67                           if(parent_item == null) {
    68                                 parent_item = (HelpItem) known_entries.get(parent_index + ".0");
    69                           }
    70                           // Otherwise the parent item must be the root.
    71                           if(parent_item == null) {
    72                                 parent_item = (HelpItem) root;
    73                           }
    74                           // Filename has - instead of .
    75                           String file_name = index_str.replace('.', '-');
    76                           // Create the new item
    77                           HelpItem item;
    78                           File temp_file = new File(Utility.HELP_DIR + file_name + ".xml");
    79                           if(temp_file.exists()) {
    80                                 item = new HelpItem(index_str + " " + title, Utility.HELP_DIR + file_name + ".xml");
    81                           }
    82                           else {
    83                                 item = new HelpItem(index_str + " " + title, Utility.HELP_DIR + file_name + ".htm");
    84                           }
    85                           temp_file = null;
    86                           // Insert into parent
    87                           insertNodeInto(item, parent_item, parent_item.getChildCount());
    88                           // Record in known entries
    89                           known_entries.put(index_str, item);
    90                           item = null;
    91                           file_name = null;
    92                           parent_item = null;
    93                           parent_index = null;
    94                      }
    95                      title = null;
    96                      index_str = null;
    97                      tokenizer = null;
    98                 }
    99                 known_entries.clear();
    100                 known_entries = null;
    101                 line = null;
    102                 buffered_reader.close();
    103                 buffered_reader = null;
    104                 file_reader.close();
    105                 file_reader = null;
    106           }
    107           catch (Exception error) {
    108                 System.err.println("Cannot build help contents. Possible error in index.txt");
    109                 error.printStackTrace();
    110           }
    111     }
     40    extends DefaultTreeModel {
     41    /** Constructor that builds the contents model.
     42     * @see org.greenstone.gatherer.help.HelpItem
     43     */
     44    public ContentModel() {
     45    super(new HelpItem("Contents"));
     46    // Load the contents file and build the contents structure found within.
     47    try {
     48        FileReader file_reader = new FileReader(Utility.HELP_DIR + "index.txt");
     49        BufferedReader buffered_reader = new BufferedReader(file_reader);
     50        String line = null;
     51        HashMap known_entries = new HashMap();
     52        while((line = buffered_reader.readLine()) != null) {
     53        // We'll use my ever popular command tokenizer to ensure we can parse index | title.
     54        CommandTokenizer tokenizer = new CommandTokenizer(line);
     55        // The first thing off the tokenizer is an index of the form xx.xx.xx
     56        String index_str = tokenizer.nextToken();
     57        // And the second is a title for this entry
     58        String title = tokenizer.nextToken();
     59        // Now we determine the parent of this entry. In general this involves removing the last portion of the index so 2.3 becomes 2. However there is a special case when the index has only one part, ie 2, in which case it get added to the root, then the index is replaced with 2.0.
     60        int position = -1;
     61        if((position = index_str.lastIndexOf(".")) != -1) {
     62            // Find parent index
     63            String parent_index = index_str.substring(0, position);
     64            // Retrieve parent item
     65            HelpItem parent_item = (HelpItem) known_entries.get(parent_index);
     66            // Otherwise append ".0" and search again...
     67            if(parent_item == null) {
     68            parent_item = (HelpItem) known_entries.get(parent_index + ".0");
     69            }
     70            // Otherwise the parent item must be the root.
     71            if(parent_item == null) {
     72            parent_item = (HelpItem) root;
     73            }
     74            // Filename has - instead of .
     75            String file_name = index_str.replace('.', '-');
     76            // Create the new item
     77            HelpItem item;
     78            File temp_file = new File(Utility.HELP_DIR + file_name + ".xml");
     79            if(temp_file.exists()) {
     80            item = new HelpItem(index_str + " " + title, Utility.HELP_DIR + file_name + ".xml");
     81            }
     82            else {
     83            item = new HelpItem(index_str + " " + title, Utility.HELP_DIR + file_name + ".htm");
     84            }
     85            temp_file = null;
     86            // Insert into parent
     87            insertNodeInto(item, parent_item, parent_item.getChildCount());
     88            // Record in known entries
     89            known_entries.put(index_str, item);
     90            item = null;
     91            file_name = null;
     92            parent_item = null;
     93            parent_index = null;
     94        }
     95        title = null;
     96        index_str = null;
     97        tokenizer = null;
     98        }
     99        known_entries.clear();
     100        known_entries = null;
     101        line = null;
     102        buffered_reader.close();
     103        buffered_reader = null;
     104        file_reader.close();
     105        file_reader = null;
     106    }
     107    catch (Exception error) {
     108        System.err.println("Cannot build help contents. Possible error in index.txt");
     109        error.printStackTrace();
     110    }
     111    }
    112112}
    113 
  • trunk/gli/src/org/greenstone/gatherer/help/HelpFrame.java

    r4293 r4365  
    7575 */
    7676public class HelpFrame
    77     extends JFrame {
    78     /** The html rendering pane. */
    79     private CalHTMLPane view = null;
    80     private ContentModel model = null;
     77    extends JFrame {
     78    /** The html rendering pane. */
     79    private CalHTMLPane view = null;
     80    private ContentModel model = null;
    8181     
    82     private JSplitPane split_pane = null;
    83     /** A contents tree for the left of the frame. */
    84     private JTree contents = null;
    85     /** The size of a button on this pane. */
    86     static final Dimension BUTTON_SIZE = new Dimension(100,50);
    87     /** The minimum size of any component in this frame. */
    88     static final Dimension MIN_SIZE = new Dimension(100,100);
    89     /** The size of the frame itself. */
    90     static final Dimension SIZE = new Dimension(760,560);
    91     /** Constructor.
    92       * @param file_name The file name, as a <Strong>String</strong> of the help page to initially show.
     82    private JSplitPane split_pane = null;
     83    /** A contents tree for the left of the frame. */
     84    private JTree contents = null;
     85    /** The size of a button on this pane. */
     86    static final Dimension BUTTON_SIZE = new Dimension(100,50);
     87    /** The minimum size of any component in this frame. */
     88    static final Dimension MIN_SIZE = new Dimension(100,100);
     89    /** The size of the frame itself. */
     90    static final Dimension SIZE = new Dimension(760,560);
     91    /** Constructor.
     92     * @param file_name The file name, as a <Strong>String</strong> of the help page to initially show.
    9393      */
    94     public HelpFrame() {
    95           setDefaultCloseOperation(HIDE_ON_CLOSE);
    96           setSize(SIZE);
    97           model = new ContentModel();
    98           contents = new JTree(model);
    99           contents.addTreeSelectionListener(new ContentsListener());
    100           contents.setExpandsSelectedPaths(true);
    101           view = new CalHTMLPane();
    102           // Creation
    103           JPanel content_pane = (JPanel) this.getContentPane();
    104           split_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
    105           JScrollPane index_scroll = new JScrollPane(contents);
    106           JScrollPane view_scroll = new JScrollPane(view);
    107           // Layout
    108           split_pane.add(index_scroll, JSplitPane.LEFT);
    109           split_pane.add(view_scroll, JSplitPane.RIGHT);
    110           content_pane.setLayout(new BorderLayout());
    111           content_pane.add(split_pane, BorderLayout.CENTER);
    112           // Center
    113           Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
    114           setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);         
    115     }
    116     /** Destructor. Removes all listeners, closes streams and explicitly sets data members to null. */
    117     public void destroy() {
    118           view = null;
    119           contents = null;
    120     }
    121     public void setView(String index) {
    122           try {
     94    public HelpFrame() {
     95    setDefaultCloseOperation(HIDE_ON_CLOSE);
     96    setSize(SIZE);
     97    model = new ContentModel();
     98    contents = new JTree(model);
     99    contents.addTreeSelectionListener(new ContentsListener());
     100    contents.setExpandsSelectedPaths(true);
     101    view = new CalHTMLPane();
     102    // Creation
     103    JPanel content_pane = (JPanel) this.getContentPane();
     104    split_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
     105    JScrollPane index_scroll = new JScrollPane(contents);
     106    JScrollPane view_scroll = new JScrollPane(view);
     107    // Layout
     108    split_pane.add(index_scroll, JSplitPane.LEFT);
     109    split_pane.add(view_scroll, JSplitPane.RIGHT);
     110    content_pane.setLayout(new BorderLayout());
     111    content_pane.add(split_pane, BorderLayout.CENTER);
     112    // Center
     113    Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
     114    setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);       
     115    }
     116    /** Destructor. Removes all listeners, closes streams and explicitly sets data members to null. */
     117    public void destroy() {
     118    view = null;
     119    contents = null;
     120    }
     121    public void setView(String index) {
     122    try {
    123123                // Retrieve the node at index
    124                 HelpItem current = (HelpItem) model.getRoot();
    125                 StringTokenizer tokenizer = new StringTokenizer(index, ".");
    126                 while(tokenizer.hasMoreTokens()) {
    127                      String token = tokenizer.nextToken();
    128                      int position = Integer.parseInt(token);
    129                      if(position > 0 && position <= current.getChildCount()) {
    130                           current = (HelpItem) current.getChildAt(position - 1);
    131                      }
    132                      token = null;
    133                 }
    134                 tokenizer = null;
     124        HelpItem current = (HelpItem) model.getRoot();
     125        StringTokenizer tokenizer = new StringTokenizer(index, ".");
     126        while(tokenizer.hasMoreTokens()) {
     127        String token = tokenizer.nextToken();
     128        int position = Integer.parseInt(token);
     129        if(position > 0 && position <= current.getChildCount()) {
     130            current = (HelpItem) current.getChildAt(position - 1);
     131        }
     132        token = null;
     133        }
     134        tokenizer = null;
    135135                // Ensure path is selected
    136                 TreePath path = new TreePath(current.getPath());
    137                 contents.setSelectionPath(path);
    138                 contents.scrollPathToVisible(path);
    139                 path = null;
     136        TreePath path = new TreePath(current.getPath());
     137        contents.setSelectionPath(path);
     138        contents.scrollPathToVisible(path);
     139        path = null;
    140140                // Build url
    141                 URL url = current.getURL();
    142                 current = null;
     141        URL url = current.getURL();
     142        current = null;
    143143                // Show help dialog
    144                 show();
    145                 split_pane.setDividerLocation(0.2);
     144        show();
     145        split_pane.setDividerLocation(0.2);
    146146                // Display help page
    147                 if(url != null) {
    148                      view.showHTMLDocument(url);
    149                 }
    150                 url = null;
    151           }
    152           catch (Exception error) {
    153                 show();
    154                 split_pane.setDividerLocation(0.2);
    155           }
    156     }
     147        if(url != null) {
     148        view.showHTMLDocument(url);
     149        }
     150        url = null;
     151    }
     152    catch (Exception error) {
     153        show();
     154        split_pane.setDividerLocation(0.2);
     155    }
     156    }
    157157
    158     /** This listener is used to lister for selection changes in the contents tree, and to show the appropriate page as required.
     158    /** This listener is used to lister for selection changes in the contents tree, and to show the appropriate page as required.
    159159      */
    160     private class ContentsListener
    161           implements TreeSelectionListener {
    162           /** Any implementation of <i>TreeSelectionListener</i> must include this method so we can be informed when the tree selection changes.
    163             * @param event A <strong>TreeSelectionEvent</strong> containing al the relevant information about the event itself.
     160    private class ContentsListener
     161    implements TreeSelectionListener {
     162    /** Any implementation of <i>TreeSelectionListener</i> must include this method so we can be informed when the tree selection changes.
     163     * @param event A <strong>TreeSelectionEvent</strong> containing al the relevant information about the event itself.
    164164            */
    165           public void valueChanged(TreeSelectionEvent event) {
    166                 TreePath path = event.getPath();
    167                 HelpItem node = (HelpItem)path.getLastPathComponent();
    168                 URL url = node.getURL();
    169                 if(url != null) {
    170                      view.showHTMLDocument(url);
    171                 }
    172           }
    173     }
     165    public void valueChanged(TreeSelectionEvent event) {
     166        TreePath path = event.getPath();
     167        HelpItem node = (HelpItem)path.getLastPathComponent();
     168        URL url = node.getURL();
     169        if(url != null) {
     170        view.showHTMLDocument(url);
     171        }
     172    }
     173    }
    174174}
  • trunk/gli/src/org/greenstone/gatherer/help/HelpItem.java

    r4293 r4365  
    5959/** This class provides a wrapper around a <strong>DefaultMutableTreeNode</strong> which provides the ability to set an html page to be loaded when this node is selected. */
    6060public class HelpItem
    61     extends DefaultMutableTreeNode {
    62     /** The file name of the html page to load. */
    63     public String file_name = null;
    64     /** The title to be displayed for this tree node. */
    65     public String title = null;
    66     /** Default Construtor with no file and no title. */
    67     public HelpItem() {
    68           this.file_name = null;
    69           this.title = "No Content";
    70     }
    71     /** Constructor.
     61    extends DefaultMutableTreeNode {
     62    /** The file name of the html page to load. */
     63    public String file_name = null;
     64    /** The title to be displayed for this tree node. */
     65    public String title = null;
     66    /** Default Construtor with no file and no title. */
     67    public HelpItem() {
     68    this.file_name = null;
     69    this.title = "No Content";
     70    }
     71    /** Constructor.
    7272      * @param title The title to be shown for this node as a <strong>String</strong>.
    7373      */
    74     public HelpItem(String title) {
    75           this.file_name = null;
    76           this.title = title;
    77     }
    78     /** Constructor.
     74    public HelpItem(String title) {
     75    this.file_name = null;
     76    this.title = title;
     77    }
     78    /** Constructor.
    7979      * @param title The title to be shown for this node as a <strong>String</strong>.
    8080      * @param file_name The name of the html file as a <strong>String</strong>.
    8181      */
    82     public HelpItem(String title, String file_name) {
    83           this.file_name = file_name;
    84           this.title = title;
     82    public HelpItem(String title, String file_name) {
     83    this.file_name = file_name;
     84    this.title = title;
    8585
    86           file_name = file_name.replace('/', File.separatorChar);
    87           file_name = file_name.replace('\\', File.separatorChar);
    88     }
    89     /** This method reads in the html file then returns it as one long string.
     86    file_name = file_name.replace('/', File.separatorChar);
     87    file_name = file_name.replace('\\', File.separatorChar);
     88    }
     89    /** This method reads in the html file then returns it as one long string.
    9090      * @return A <strong>String</strong> containing the entire html files contents.
    9191      */
    92     public String content() {
    93           String content = "";
    94           try {
    95                 File file = new File(file_name);
    96                 FileReader in_reader = new FileReader(file);
    97                 BufferedReader in = new BufferedReader(in_reader,
    98                                                                     Utility.BUFFER_SIZE);
    99                 String line;
    100                 while((line = in.readLine()) != null) {
    101                      content = content + line;
    102                 }
    103           }
    104           catch (Exception e) {
    105           }
    106           return content;
    107     }
    108     /** Method to get the file name.
     92    public String content() {
     93    String content = "";
     94    try {
     95        File file = new File(file_name);
     96        FileReader in_reader = new FileReader(file);
     97        BufferedReader in = new BufferedReader(in_reader,
     98                           Utility.BUFFER_SIZE);
     99        String line;
     100        while((line = in.readLine()) != null) {
     101        content = content + line;
     102        }
     103    }
     104    catch (Exception e) {
     105    }
     106    return content;
     107    }
     108    /** Method to get the file name.
    109109      * @return The file name as a <strong>String</strong>.
    110110      */
    111     public String getFileName() {
    112           return file_name;
    113     }
    114     /** Method to create a url from the file name. This method is preferred over the <i>content()</i> method, as the url can then be directly parsed by the html renderer.
     111    public String getFileName() {
     112    return file_name;
     113    }
     114    /** Method to create a url from the file name. This method is preferred over the <i>content()</i> method, as the url can then be directly parsed by the html renderer.
    115115      * @return A <strong>URL</strong> that points to the files location as determined by the file name.
    116116      */
    117     public URL getURL() {
    118           URL url = null;
    119           if(file_name != null) {
    120                 try {
    121                      File file = new File(file_name);
    122                      url = file.toURL();
    123                 }
    124                 catch (Exception e) {
    125                 }
    126           }
    127           return url;
    128     }
    129     /** Method to acquire a string representation of this node, which in this case is its title.
     117    public URL getURL() {
     118    URL url = null;
     119    if(file_name != null) {
     120        try {
     121        File file = new File(file_name);
     122        url = file.toURL();
     123        }
     124        catch (Exception e) {
     125        }
     126    }
     127    return url;
     128    }
     129    /** Method to acquire a string representation of this node, which in this case is its title.
    130130      * @return A <strong>String</strong> which shows how this node should be displayed in the tree for example.
    131131      */
    132     public String toString() {
    133           return title;
    134     }
     132    public String toString() {
     133    return title;
     134    }
    135135}
    136 
    137 
    138 
  • trunk/gli/src/org/greenstone/gatherer/mem/Attribute.java

    r4293 r4365  
    4444
    4545public class Attribute
    46     implements Comparable {
    47     public String language = null;
    48     public String name = null;
    49     public String value = null;
    50     public String text = null;
    51     private boolean language_specific = false;
    52     public Attribute(String name, String value) {
    53           this.name = name;
    54           this.value = value;
    55     }
    56     public Attribute(String name, String language, String value) {
    57           this.language = language;
    58           this.language_specific = true;
    59           this.name = name;
    60           this.value = value;
    61     }
    62     public int compareTo(Object other) {
    63           return toString().compareTo(other.toString());
    64     }
    65     public boolean equals(Object other) {
    66           return toString().equals(other.toString());
    67     }
    68     public String get(int column) {
    69           switch(column) {
    70           case 0:
    71                 return name;
    72           case 1:
    73                 if(language_specific) {
    74                      return language;
    75                 }
    76           default:
    77                 return value;
    78           }
    79     }
    80     public String toString() {
    81           if(text == null) {
    82                 if(language_specific) {
    83                      text = name + " [" + language + "] = " + value;
    84                 }
    85                 else {
    86                      text = name + " = " + value;
    87                 }
    88           }
    89           return text;
    90     }
     46    implements Comparable {
     47    public String language = null;
     48    public String name = null;
     49    public String value = null;
     50    public String text = null;
     51    private boolean language_specific = false;
     52    public Attribute(String name, String value) {
     53    this.name = name;
     54    this.value = value;
     55    }
     56    public Attribute(String name, String language, String value) {
     57    this.language = language;
     58    this.language_specific = true;
     59    this.name = name;
     60    this.value = value;
     61    }
     62    public int compareTo(Object other) {
     63    return toString().compareTo(other.toString());
     64    }
     65    public boolean equals(Object other) {
     66    return toString().equals(other.toString());
     67    }
     68    public String get(int column) {
     69    switch(column) {
     70    case 0:
     71        return name;
     72    case 1:
     73        if(language_specific) {
     74        return language;
     75        }
     76    default:
     77        return value;
     78    }
     79    }
     80    public String toString() {
     81    if(text == null) {
     82        if(language_specific) {
     83        text = name + " [" + language + "] = " + value;
     84        }
     85        else {
     86        text = name + " = " + value;
     87        }
     88    }
     89    return text;
     90    }
    9191}
  • trunk/gli/src/org/greenstone/gatherer/mem/AttributeTableModel.java

    r4293 r4365  
    4848 */
    4949public class AttributeTableModel
    50     extends AbstractTableModel {
    51     private Attribute last_attribute = null;
    52     private int last_index = -1;
    53     private int longest[] = null;
    54     private int column_zero_width = 0;
    55     private JScrollPane scroll_pane = null;
    56     private JTable table = null;
    57     private String default_value = null;
    58     private String names[] = null;
    59     private TreeSet attributes = null;
    60     static final private int table_width = 550;
    61 
    62     public AttributeTableModel(TreeSet attributes, String one, String two, String default_value) {
    63           super();
    64           this.attributes = attributes;
    65           this.default_value = default_value;
    66           this.longest = new int[2];
    67           this.names = new String[2];
    68           names[0] = one;
    69           names[1] = two;
    70     }
    71 
    72     public AttributeTableModel(TreeSet attributes, String one, String two, String three, String default_value) {
    73           super();
    74           this.attributes = attributes;
    75           this.default_value = default_value;
    76           this.longest = new int[3];
    77           this.names = new String[3];
    78           names[0] = one;
    79           names[1] = two;
    80           names[2] = three;
    81     }
    82     public void add(Attribute attribute) {
    83           attributes.add(attribute);
    84           // Determine the row index
    85           boolean found = false;
    86           int index = 0;
    87           Iterator iterator = attributes.iterator();
    88           for(int i = 0; !found && iterator.hasNext(); i++) {
    89                 if(attribute == iterator.next()) {
    90                      found = true;
    91                      index = i;
    92                 }
    93           }
    94           // Fire event
    95           fireTableRowsInserted(index, index);
    96     }
    97 
    98     public boolean contains(String value, int column) {
    99           boolean result = false;
    100           Iterator iterator = attributes.iterator();
    101           while(iterator.hasNext() && !result) {
    102                 Attribute attribute = (Attribute) iterator.next();
    103                 if(value.equals(attribute.get(column))) {
    104                      result = true;
    105                 }
    106          
    107           return result;
    108     }
    109 
    110     public Attribute getAttribute(int row) {
    111           if(row != last_index) {
    112                 last_index = row;
    113                 last_attribute = null;
     50    extends AbstractTableModel {
     51    private Attribute last_attribute = null;
     52    private int last_index = -1;
     53    private int longest[] = null;
     54    private int column_zero_width = 0;
     55    private JScrollPane scroll_pane = null;
     56    private JTable table = null;
     57    private String default_value = null;
     58    private String names[] = null;
     59    private TreeSet attributes = null;
     60    static final private int table_width = 550;
     61
     62    public AttributeTableModel(TreeSet attributes, String one, String two, String default_value) {
     63    super();
     64    this.attributes = attributes;
     65    this.default_value = default_value;
     66    this.longest = new int[2];
     67    this.names = new String[2];
     68    names[0] = one;
     69    names[1] = two;
     70    }
     71
     72    public AttributeTableModel(TreeSet attributes, String one, String two, String three, String default_value) {
     73    super();
     74    this.attributes = attributes;
     75    this.default_value = default_value;
     76    this.longest = new int[3];
     77    this.names = new String[3];
     78    names[0] = one;
     79    names[1] = two;
     80    names[2] = three;
     81    }
     82    public void add(Attribute attribute) {
     83    attributes.add(attribute);
     84    // Determine the row index
     85    boolean found = false;
     86    int index = 0;
     87    Iterator iterator = attributes.iterator();
     88    for(int i = 0; !found && iterator.hasNext(); i++) {
     89        if(attribute == iterator.next()) {
     90        found = true;
     91        index = i;
     92        }
     93    }
     94    // Fire event
     95    fireTableRowsInserted(index, index);
     96    }
     97
     98    public boolean contains(String value, int column) {
     99    boolean result = false;
     100    Iterator iterator = attributes.iterator();
     101    while(iterator.hasNext() && !result) {
     102        Attribute attribute = (Attribute) iterator.next();
     103        if(value.equals(attribute.get(column))) {
     104        result = true;
     105        }
     106   
     107    return result;
     108    }
     109
     110    public Attribute getAttribute(int row) {
     111    if(row != last_index) {
     112        last_index = row;
     113        last_attribute = null;
    114114                // Retrieve the value from the set using its iterator
    115                 Iterator iterator = attributes.iterator();
    116                 for(int i = 0; i < row && iterator.hasNext(); i++) {
    117                      iterator.next();
    118                 }
    119                 if(iterator.hasNext()) {
    120                      last_attribute = (Attribute) iterator.next();
    121                 }
    122                 iterator = null;
    123           }
    124           return last_attribute;
    125     }
    126 
    127     public int getColumnCount() {
    128           return names.length;
    129     }
    130 
    131     public String getColumnName(int col) {
    132           return names[col];
    133     }
    134 
    135     public int getRowCount() {
    136           int count = 0;
    137           if(attributes != null) {
    138                 count = attributes.size();
    139           }
    140           return count;
    141     }
    142 
    143     public Object getValueAt(int row, int col) {
    144           String value = null;
    145           Attribute attribute = getAttribute(row);
    146           if(attribute != null) {
    147                 value = attribute.get(col);
    148           }
    149           if(value == null) {
    150                 value = default_value;
    151           }
    152           // Update table column width if necessary
    153           if(value.length() > longest[col]) {
    154                 Component component = table.getCellRenderer(row, col).getTableCellRendererComponent(table, value, false, false, 0, 0);
    155                 int width = component.getPreferredSize().width + 10;
    156                 TableColumn column = table.getColumnModel().getColumn(col);
    157                 if(width > column.getPreferredWidth()) {
    158                      // Calculate default column width
    159                      int default_width = 0;
    160                      int total_width = scroll_pane.getSize().width;
    161                      if(names.length == 2) {
    162                           if(col == 0) {
    163                                 default_width = total_width / 3;
    164                           }
    165                           else {
    166                                 default_width = 2 * (total_width / 3);
    167                           }
    168                      }
    169                      else {
    170                           switch(col) {
    171                           case 0:
    172                                 default_width = (2 * total_width) / 9;
    173                                 break;
    174                           case 1:
    175                                 default_width = total_width / 9;
    176                                 break;
    177                           default:
    178                                 default_width = 2 * (total_width / 3);
    179                                 break;
    180                           }
    181                      }
    182                      if(width > default_width) {
    183                           column.setPreferredWidth(width);
    184                      }
    185                      else {
    186                           column.setPreferredWidth(default_width);
    187                      }
    188                 }
    189                 longest[col] = value.length();
    190           }
    191           return Utility.stripNL(value);
    192     }
    193     public void removeRow(final int row) {
    194           ///ystem.err.println("Remove row " + row);
    195           Attribute attribute = getAttribute(row);
    196           if(attribute != null) {
    197                 attributes.remove(attribute);
     115        Iterator iterator = attributes.iterator();
     116        for(int i = 0; i < row && iterator.hasNext(); i++) {
     117        iterator.next();
     118        }
     119        if(iterator.hasNext()) {
     120        last_attribute = (Attribute) iterator.next();
     121        }
     122        iterator = null;
     123    }
     124    return last_attribute;
     125    }
     126
     127    public int getColumnCount() {
     128    return names.length;
     129    }
     130
     131    public String getColumnName(int col) {
     132    return names[col];
     133    }
     134
     135    public int getRowCount() {
     136    int count = 0;
     137    if(attributes != null) {
     138        count = attributes.size();
     139    }
     140    return count;
     141    }
     142
     143    public Object getValueAt(int row, int col) {
     144    String value = null;
     145    Attribute attribute = getAttribute(row);
     146    if(attribute != null) {
     147        value = attribute.get(col);
     148    }
     149    if(value == null) {
     150        value = default_value;
     151    }
     152    // Update table column width if necessary
     153    if(value.length() > longest[col]) {
     154        Component component = table.getCellRenderer(row, col).getTableCellRendererComponent(table, value, false, false, 0, 0);
     155        int width = component.getPreferredSize().width + 10;
     156        TableColumn column = table.getColumnModel().getColumn(col);
     157        if(width > column.getPreferredWidth()) {
     158        // Calculate default column width
     159        int default_width = 0;
     160        int total_width = scroll_pane.getSize().width;
     161        if(names.length == 2) {
     162            if(col == 0) {
     163            default_width = total_width / 3;
     164            }
     165            else {
     166            default_width = 2 * (total_width / 3);
     167            }
     168        }
     169        else {
     170            switch(col) {
     171            case 0:
     172            default_width = (2 * total_width) / 9;
     173            break;
     174            case 1:
     175            default_width = total_width / 9;
     176            break;
     177            default:
     178            default_width = 2 * (total_width / 3);
     179            break;
     180            }
     181        }
     182        if(width > default_width) {
     183            column.setPreferredWidth(width);
     184        }
     185        else {
     186            column.setPreferredWidth(default_width);
     187        }
     188        }
     189        longest[col] = value.length();
     190    }
     191    return Utility.stripNL(value);
     192    }
     193    public void removeRow(final int row) {
     194    ///ystem.err.println("Remove row " + row);
     195    Attribute attribute = getAttribute(row);
     196    if(attribute != null) {
     197        attributes.remove(attribute);
    198198                // Arg... this caused me a headache for hours.
    199                 last_index = -1;
    200                 fireTableRowsDeleted(row, row);
    201           }
    202     }
    203     public void setScrollPane(JScrollPane scroll_pane) {
    204           this.scroll_pane = scroll_pane;
    205     }
    206     public void setTable(JTable table) {
    207           this.table = table;
    208           this.longest = new int[getColumnCount()];
    209     }
     199        last_index = -1;
     200        fireTableRowsDeleted(row, row);
     201    }
     202    }
     203    public void setScrollPane(JScrollPane scroll_pane) {
     204    this.scroll_pane = scroll_pane;
     205    }
     206    public void setTable(JTable table) {
     207    this.table = table;
     208    this.longest = new int[getColumnCount()];
     209    }
    210210}
  • trunk/gli/src/org/greenstone/gatherer/mem/MEMModel.java

    r4293 r4365  
    4646 */
    4747public class MEMModel
    48     extends DefaultTreeModel {
     48    extends DefaultTreeModel {
    4949
    50     public MEMModel() {
    51           super(new MEMNode());
    52     }
     50    public MEMModel() {
     51    super(new MEMNode());
     52    }
    5353
    54     public void add(MEMNode parent, Object object, int type) {
    55           if(parent == null) {
    56                 parent = (MEMNode) root;
    57           }
    58           MEMNode new_node = new MEMNode(type, object, null);
    59           SynchronizedTreeModelTools.insertNodeInto(this, parent, new_node);
    60     }
     54    public void add(MEMNode parent, Object object, int type) {
     55    if(parent == null) {
     56        parent = (MEMNode) root;
     57    }
     58    MEMNode new_node = new MEMNode(type, object, null);
     59    SynchronizedTreeModelTools.insertNodeInto(this, parent, new_node);
     60    }
    6161
    62     public MEMNode getProfileNode() {
    63           MEMNode node = (MEMNode) root;
    64           return (MEMNode) node.getChildAt(node.getChildCount() - 1);
    65     }
     62    public MEMNode getProfileNode() {
     63    MEMNode node = (MEMNode) root;
     64    return (MEMNode) node.getChildAt(node.getChildCount() - 1);
     65    }
    6666
    67     public void remove(String name, int type) {
    68           MEMNode current = (MEMNode) root;
    69           for(int i = 0; i < current.getChildCount(); i++) {
    70                 MEMNode child = (MEMNode) current.getChildAt(i);
    71                 if(child.toString().equals(name) && type == MEMNode.SET) {
    72                      // Place on AWT event thread to avoid errors.
    73                      SynchronizedTreeModelTools.removeNodeFromParent(this, child);
    74                      return;
    75                 }
     67    public void remove(String name, int type) {
     68    MEMNode current = (MEMNode) root;
     69    for(int i = 0; i < current.getChildCount(); i++) {
     70        MEMNode child = (MEMNode) current.getChildAt(i);
     71        if(child.toString().equals(name) && type == MEMNode.SET) {
     72        // Place on AWT event thread to avoid errors.
     73        SynchronizedTreeModelTools.removeNodeFromParent(this, child);
     74        return;
     75        }
    7676                // This may be the profiler root node, in which case we descend into its children if we're looking for collection file matches.
    77                 else if(type == MEMNode.COLLECTION && child.getType() == MEMNode.PROFILER) {
    78                      for(int j = 0; j < child.getChildCount(); j++) {
    79                           MEMNode inner_child = (MEMNode) child.getChildAt(j);
    80                           if(inner_child.toString().equals(name)) {
    81                                 // Place on AWT event thread to avoid errors.
    82                                 SynchronizedTreeModelTools.removeNodeFromParent(this, inner_child);
    83                                 return;
    84                           }
    85                      }
    86                 }
     77        else if(type == MEMNode.COLLECTION && child.getType() == MEMNode.PROFILER) {
     78        for(int j = 0; j < child.getChildCount(); j++) {
     79            MEMNode inner_child = (MEMNode) child.getChildAt(j);
     80            if(inner_child.toString().equals(name)) {
     81            // Place on AWT event thread to avoid errors.
     82            SynchronizedTreeModelTools.removeNodeFromParent(this, inner_child);
     83            return;
     84            }
     85        }
     86        }
    8787                // If this is a set and we are looking for an element, then iterate through its children. Can't really do this recursively.
    88                 else if(type == MEMNode.ELEMENT && child.getType() == MEMNode.SET) {
    89                      for(int j = 0; j < child.getChildCount(); j++) {
    90                           MEMNode inner_child = (MEMNode) child.getChildAt(j);
    91                           if(inner_child.toString().equals(name)) {
    92                                 // Place on AWT event thread to avoid errors.
    93                                 SynchronizedTreeModelTools.removeNodeFromParent(this, inner_child);
    94                                 return;
    95                           }
    96                      }
    97                 }
    98           }
    99     }
     88        else if(type == MEMNode.ELEMENT && child.getType() == MEMNode.SET) {
     89        for(int j = 0; j < child.getChildCount(); j++) {
     90            MEMNode inner_child = (MEMNode) child.getChildAt(j);
     91            if(inner_child.toString().equals(name)) {
     92            // Place on AWT event thread to avoid errors.
     93            SynchronizedTreeModelTools.removeNodeFromParent(this, inner_child);
     94            return;
     95            }
     96        }
     97        }
     98    }
     99    }
    100100}
  • trunk/gli/src/org/greenstone/gatherer/mem/MEMNode.java

    r4293 r4365  
    4949 */
    5050public class MEMNode
    51     extends DefaultMutableTreeNode {
    52     private AttributeTableModel model = null;
    53     private int type = 0;
    54     static final public int COLLECTION = 0;
    55     static final public int ELEMENT    = 1;
    56     static final public int PROFILER   = 2;
    57     static final public int ROOT       = 3;
    58     static final public int SET        = 4;
     51    extends DefaultMutableTreeNode {
     52    private AttributeTableModel model = null;
     53    private int type = 0;
     54    static final public int COLLECTION = 0;
     55    static final public int ELEMENT    = 1;
     56    static final public int PROFILER   = 2;
     57    static final public int ROOT       = 3;
     58    static final public int SET        = 4;
    5959
    60     public MEMNode() {
    61           this.type = ROOT;
    62     }
     60    public MEMNode() {
     61    this.type = ROOT;
     62    }
    6363
    64     public MEMNode(int type, Object object, MEMNode parent) {
    65           this.userObject = object;
    66           this.parent = parent;
    67           this.type = type;
    68     }
     64    public MEMNode(int type, Object object, MEMNode parent) {
     65    this.userObject = object;
     66    this.parent = parent;
     67    this.type = type;
     68    }
    6969
    70     public Enumeration children() {
    71           if(children == null) {
    72                 mapChildren();
    73           }       
    74           return children.elements();
    75     }
     70    public Enumeration children() {
     71    if(children == null) {
     72        mapChildren();
     73    }         
     74    return children.elements();
     75    }
    7676
    77     public boolean getAllowsChildren() {
    78           return true;
    79     }
     77    public boolean getAllowsChildren() {
     78    return true;
     79    }
    8080
    81     public TreeNode getChildAt(int index) {
    82           if(children == null) {
    83                 mapChildren();
    84           }
    85           return (TreeNode) children.get(index);
    86     }
    87     public int getChildCount() {
    88           if(children == null) {
    89                 mapChildren();
    90           }
    91           return children.size();
    92     }
    93     public ElementWrapper getElement() {
    94           ElementWrapper result = null;
    95           if(type == ELEMENT) {
    96                 result = (ElementWrapper) userObject;
    97           }
    98           return result;
    99     }
    100     public int getIndex(TreeNode node) {
    101           if(children == null) {
    102                 mapChildren();
    103           }
    104           return children.indexOf(node);
    105     }
    106     public AttributeTableModel getModel() {
    107           return model;
    108     }
    109     public TreeNode getParent() {
    110           return parent;
    111     }
    112     public MetadataSet getSet() {
    113           MetadataSet result = null;
    114           if(type == SET) {
    115                 result = (MetadataSet) userObject;
    116           }
    117           return result;
    118     }
    119     public int getType() {
    120           return type;
    121     }
    122     public boolean isLeaf() {
    123           if(children == null) {
    124                 mapChildren();
    125           }
    126           return children.size() == 0;
    127     }
    128     public void setModel(AttributeTableModel model) {
    129           this.model = model;
    130     }
    131     public String toString() {
    132           String text = "error";
    133           if(userObject != null) {
    134                 if(userObject instanceof ElementWrapper) {
    135                      text = ((ElementWrapper)userObject).getName();
    136                 }
    137                 else {
    138                      text = userObject.toString();
    139                 }
    140           }
    141           return text;
    142     }
    143     private void mapChildren() {
    144           ///ystem.err.println("Mapping the children of " + this);
    145           children = new Vector();
    146           // How we build children depends on the node type
    147           switch(type) {
    148           case PROFILER: // Add the collections as children
    149                 ArrayList a = Gatherer.c_man.msm.profiler.getCollections();
    150                 for(int i = 0; i < a.size(); i++) {
    151                      children.add(new MEMNode(COLLECTION, a.get(i), this));
    152                 }
    153                 a = null;
    154                 break;
    155           case ROOT:
    156                 Vector v = Gatherer.c_man.msm.getSets();
    157                 for(int i = 0; i < v.size(); i++) {
    158                      children.add(new MEMNode(SET, v.get(i), this));
    159                 }
    160                 v = null;
     81    public TreeNode getChildAt(int index) {
     82    if(children == null) {
     83        mapChildren();
     84    }
     85    return (TreeNode) children.get(index);
     86    }
     87    public int getChildCount() {
     88    if(children == null) {
     89        mapChildren();
     90    }
     91    return children.size();
     92    }
     93    public ElementWrapper getElement() {
     94    ElementWrapper result = null;
     95    if(type == ELEMENT) {
     96        result = (ElementWrapper) userObject;
     97    }
     98    return result;
     99    }
     100    public int getIndex(TreeNode node) {
     101    if(children == null) {
     102        mapChildren();
     103    }
     104    return children.indexOf(node);
     105    }
     106    public AttributeTableModel getModel() {
     107    return model;
     108    }
     109    public TreeNode getParent() {
     110    return parent;
     111    }
     112    public MetadataSet getSet() {
     113    MetadataSet result = null;
     114    if(type == SET) {
     115        result = (MetadataSet) userObject;
     116    }
     117    return result;
     118    }
     119    public int getType() {
     120    return type;
     121    }
     122    public boolean isLeaf() {
     123    if(children == null) {
     124        mapChildren();
     125    }
     126    return children.size() == 0;
     127    }
     128    public void setModel(AttributeTableModel model) {
     129    this.model = model;
     130    }
     131    public String toString() {
     132    String text = "error";
     133    if(userObject != null) {
     134        if(userObject instanceof ElementWrapper) {
     135        text = ((ElementWrapper)userObject).getName();
     136        }
     137        else {
     138        text = userObject.toString();
     139        }
     140    }
     141    return text;
     142    }
     143    private void mapChildren() {
     144    ///ystem.err.println("Mapping the children of " + this);
     145    children = new Vector();
     146    // How we build children depends on the node type
     147    switch(type) {
     148    case PROFILER: // Add the collections as children
     149        ArrayList a = Gatherer.c_man.msm.profiler.getCollections();
     150        for(int i = 0; i < a.size(); i++) {
     151        children.add(new MEMNode(COLLECTION, a.get(i), this));
     152        }
     153        a = null;
     154        break;
     155    case ROOT:
     156        Vector v = Gatherer.c_man.msm.getSets();
     157        for(int i = 0; i < v.size(); i++) {
     158        children.add(new MEMNode(SET, v.get(i), this));
     159        }
     160        v = null;
    161161                // Add the profile set.
    162                 children.add(new MEMNode(PROFILER, Gatherer.dictionary.get("MEM.Profiles"), this));
    163                 break;
    164           case SET: // Add the elements as children
    165                 MetadataSet set = (MetadataSet) userObject;
    166                 NodeList elements = set.getElements();
    167                 set = null;
    168                 for(int i = 0; i < elements.getLength(); i++) {
    169                      children.add(new MEMNode(ELEMENT, new ElementWrapper((Element) elements.item(i)), this));
    170                 }
    171                 elements = null;
    172                 break;
    173           case COLLECTION:
    174           case ELEMENT:
    175           default:
    176           }
    177     }
     162        children.add(new MEMNode(PROFILER, Gatherer.dictionary.get("MEM.Profiles"), this));
     163        break;
     164    case SET: // Add the elements as children
     165        MetadataSet set = (MetadataSet) userObject;
     166        NodeList elements = set.getElements();
     167        set = null;
     168        for(int i = 0; i < elements.getLength(); i++) {
     169        children.add(new MEMNode(ELEMENT, new ElementWrapper((Element) elements.item(i)), this));
     170        }
     171        elements = null;
     172        break;
     173    case COLLECTION:
     174    case ELEMENT:
     175    default:
     176    }
     177    }
    178178}
  • trunk/gli/src/org/greenstone/gatherer/mem/MetadataEditorManager.java

    r4293 r4365  
    6363 */
    6464public class MetadataEditorManager
    65     extends JDialog {
    66     private AddElementActionListener add_element_action_listener = null;
    67     private AddFileActionListener add_file_action_listener = null;
    68     private AddSetActionListener add_set_action_listener = null;
    69     /** The class used to handle add or edit attribute actions has to be globally available so that we can dispose of its dialog properly. */
    70     private AddOrEditAttributeActionListener add_or_edit_attribute_action_listener = null;
    71 /** The class used to handle add or edit value actions has to be globally available so that we can dispose of its dialog properly. */
    72     private AddOrEditValueActionListener add_or_edit_value_action_listener = null;
    73     private boolean ignore = false;
    74     /** A card layout is used to switch between the two differing detail views. */
    75     private CardLayout card_layout = null;
    76     /** Um, the size of the screen I'd guess. */
    77     private Dimension screen_size = null;
    78     private ElementWrapper current_element = null;
    79     private GValueNode current_value_node = null;
    80     /** A reference to ourselves so our inner classes can dispose of us. */
    81     private MetadataEditorManager self = null;
    82     private int current_attribute = -1;
    83     private JButton add_attribute = null;
    84     private JButton add_element = null;
    85     private JButton add_file = null;
    86     private JButton add_set = null;
    87     private JButton add_value = null;
    88     private JButton close = null;
    89     private JButton edit_attribute = null;
    90     private JButton edit_value = null;
    91     private JButton remove_attribute = null;
    92     private JButton remove_element = null;
    93     private JButton remove_file = null;
    94     private JButton remove_set = null;
    95     private JButton remove_value = null;
    96     private JLabel element_name = null;
    97     private JLabel profile_name = null;
    98     private JLabel set_name = null;
    99     private JScrollPane element_attributes_scroll = null;
    100     private JScrollPane profile_attributes_scroll = null;
    101     private JScrollPane set_attributes_scroll = null;
    102     private JPanel details_pane = null;
    103     private MEMModel model = null;
    104     private MEMNode current_node = null;
    105     private MetadataSet current_set = null;
    106     private Object target = null;
    107     private SmarterTable element_attributes = null;
    108     private SmarterTable profile_attributes = null;
    109     private SmarterTable set_attributes = null;
    110     private SmarterTree element_values = null;
    111     /** A tree that represents the current metadata sets associated with this collection. */
    112     private SmarterTree mds_tree = null;
    113     private String current_collection_file = null;
    114     private String dialog_options[] = null;
    115     /** The default size of the editor dialog. */
    116     static final private Dimension ADD_ELEMENT_SIZE = new Dimension(400,125);
    117     static final private Dimension ADD_FILE_SIZE = new Dimension(400,125);
    118     static final private Dimension ADD_SET_SIZE = new Dimension(400,125);
    119     static final private Dimension ADD_OR_EDIT_ATTRIBUTE_SIZE = new Dimension(600,325);
    120     static final private Dimension ADD_OR_EDIT_VALUE_SIZE = new Dimension(600,440);
    121     static final private Dimension LABEL_SIZE = new Dimension(100,30);
    122     static final private Dimension SIZE = new Dimension(800,480);
    123     static final private String BLANK = "blank";
    124     static final private String ELEMENT = "element";
    125     static final private String PROFILE = "profile";
    126     static final private String SET = "set";
    127     /** Constructor. */
    128     public MetadataEditorManager() {
    129           super(Gatherer.g_man);
    130           this.dialog_options = new String[2];
    131           this.screen_size = Gatherer.config.screen_size;
    132           this.self = this;
    133 
    134           dialog_options[0] = Gatherer.dictionary.get("General.OK");
    135           dialog_options[1] = Gatherer.dictionary.get("General.Cancel");
    136           // Creation
    137           setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    138           setModal(true);
    139           setSize(SIZE);
    140           setTitle(get("Title"));
    141           JPanel content_pane = (JPanel) getContentPane();
    142           content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
    143          
    144           JPanel upper_pane = new JPanel();
    145           upper_pane.setOpaque(false);
    146          
    147           model = new MEMModel();
    148 
    149           JPanel mds_tree_pane = new JPanel();
    150           mds_tree_pane.setOpaque(false);
    151           mds_tree_pane.setPreferredSize(new Dimension(200,500));
    152           mds_tree = new SmarterTree(model);
    153           mds_tree.setCellRenderer(new MEMTreeCellRenderer());
    154           mds_tree.setRootVisible(false);
    155           mds_tree.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    156           mds_tree.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    157           mds_tree.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    158           mds_tree.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    159 
    160           details_pane = new JPanel();
    161           details_pane.setOpaque(false);
    162           card_layout = new CardLayout();
    163          
    164           JPanel set_details_pane = new JPanel();
    165           set_details_pane.setOpaque(false);
    166          
    167           JPanel set_name_pane = new JPanel();
    168           set_name_pane.setOpaque(false);
    169           JLabel set_name_label = new JLabel(get("Name"));
    170           set_name_label.setOpaque(false);
    171           set_name_label.setPreferredSize(LABEL_SIZE);
    172           set_name = new JLabel();
    173           set_name.setBorder(BorderFactory.createLoweredBevelBorder());
    174           set_name.setOpaque(false);
    175          
    176           JPanel set_attributes_pane = new JPanel();
    177           set_attributes_pane.setOpaque(false);
    178           set_attributes_scroll = new JScrollPane();
    179           set_attributes_scroll.getViewport().setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    180           set_attributes_scroll.setOpaque(true);
    181           set_attributes = new SmarterTable();
    182           set_attributes.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    183           set_attributes.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    184           set_attributes.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    185           set_attributes.setHeadingBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
    186           set_attributes.setHeadingForeground(Gatherer.config.getColor("coloring.collection_heading_foreground", false));
    187           set_attributes.setOpaque(false);
    188           set_attributes.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    189           set_attributes.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    190           set_attributes.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    191          
    192           JPanel element_details_pane = new JPanel();
    193           element_details_pane.setOpaque(false);
    194 
    195           JPanel element_name_pane = new JPanel();
    196           element_name_pane.setOpaque(false);
    197           JLabel element_name_label = new JLabel(get("Name"));
    198           element_name_label.setOpaque(false);
    199           element_name_label.setPreferredSize(LABEL_SIZE);
    200           element_name = new JLabel();
    201           element_name.setBorder(BorderFactory.createLoweredBevelBorder());
    202           element_name.setOpaque(false);
    203          
    204           JPanel element_inner_pane = new JPanel();
    205           element_inner_pane.setOpaque(false);
    206          
    207           JPanel element_attributes_pane = new JPanel();
    208           element_attributes_pane.setOpaque(false);
    209           element_attributes_scroll = new JScrollPane();
    210           element_attributes_scroll.getViewport().setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    211           element_attributes_scroll.setOpaque(true);
    212           element_attributes = new SmarterTable();
    213           element_attributes.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    214           element_attributes.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    215           element_attributes.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    216           element_attributes.setHeadingBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
    217           element_attributes.setHeadingForeground(Gatherer.config.getColor("coloring.collection_heading_foreground", false));
    218           element_attributes.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    219           element_attributes.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    220           element_attributes.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    221 
    222           JPanel element_values_pane = new JPanel();
    223           element_values_pane.setOpaque(false);
    224           element_values = new SmarterTree();
    225           element_values.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    226           element_values.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    227           element_values.setRootVisible(false);
    228           element_values.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    229           element_values.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    230          
    231           JPanel profile_details_pane = new JPanel();
    232           profile_details_pane.setOpaque(false);
    233           JPanel profile_name_pane = new JPanel();
    234           profile_name_pane.setOpaque(false);
    235           JLabel profile_name_label = new JLabel(get("Name"));
    236           profile_name_label.setOpaque(false);
    237           profile_name_label.setPreferredSize(LABEL_SIZE);
    238           profile_name = new JLabel();
    239           profile_name.setBorder(BorderFactory.createLoweredBevelBorder());
    240           profile_name.setOpaque(false);
    241          
    242           JPanel profile_attributes_pane = new JPanel();
    243           profile_attributes_pane.setOpaque(false);
    244           profile_attributes_scroll = new JScrollPane();
    245           profile_attributes_scroll.getViewport().setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    246           profile_attributes_scroll.setOpaque(true);
    247           profile_attributes = new SmarterTable();
    248           profile_attributes.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    249           profile_attributes.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    250           profile_attributes.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    251           profile_attributes.setHeadingBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
    252           profile_attributes.setHeadingForeground(Gatherer.config.getColor("coloring.collection_heading_foreground", false));
    253           profile_attributes.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    254           profile_attributes.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    255           profile_attributes.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    256 
    257           JPanel button_pane = new JPanel();
    258           button_pane.setOpaque(false);
    259 
    260           JPanel button_label_pane = new JPanel();
    261           button_label_pane.setOpaque(false);
    262           JLabel attribute_label = new JLabel(get("Attribute"));
    263           attribute_label.setOpaque(false);
    264           //attribute_label.setHorizontalAlignment(JLabel.CENTER);
    265           attribute_label.setPreferredSize(LABEL_SIZE);
    266           JLabel element_label = new JLabel(get("Element"));
    267           element_label.setOpaque(false);
    268           //element_label.setHorizontalAlignment(JLabel.CENTER);
    269           element_label.setPreferredSize(LABEL_SIZE);
    270           JLabel file_label = new JLabel(get("File"));
    271           file_label.setOpaque(false);
    272           //element_label.setHorizontalAlignment(JLabel.CENTER);
    273           file_label.setPreferredSize(LABEL_SIZE);
    274           JLabel set_label = new JLabel(get("Set"));
    275           set_label.setOpaque(false);
    276           //set_label.setHorizontalAlignment(JLabel.CENTER);
    277           set_label.setPreferredSize(LABEL_SIZE);
    278           JLabel value_label = new JLabel(get("Value"));
    279           value_label.setOpaque(false);
    280           //value_label.setHorizontalAlignment(JLabel.CENTER);
    281           value_label.setPreferredSize(LABEL_SIZE);
    282 
    283           JPanel inner_button_pane = new JPanel();
    284           inner_button_pane.setOpaque(false);
    285           add_attribute = new JButton(get("Add"));
    286           add_element = new JButton(get("Add"));
    287           add_file = new JButton(get("Add"));
    288           add_set = new JButton(get("Add"));
    289           add_value = new JButton(get("Add"));
    290           edit_attribute = new JButton(get("Edit"));
    291           edit_value = new JButton(get("Edit"));
    292           remove_attribute = new JButton(get("Remove"));
    293           remove_element = new JButton(get("Remove"));
    294           remove_file = new JButton(get("Remove"));
    295           remove_set = new JButton(get("Remove"));
    296           remove_value = new JButton(get("Remove"));
    297           setControls(false, false, false, false, false, false, false, false, false, false, false, false);
    298 
    299           close = new JButton(get("General.Close"));
    300 
    301           add_element_action_listener = new AddElementActionListener();
    302           add_file_action_listener = new AddFileActionListener();
    303           add_set_action_listener = new AddSetActionListener();
    304           add_or_edit_attribute_action_listener = new AddOrEditAttributeActionListener();
    305           add_or_edit_value_action_listener = new AddOrEditValueActionListener();
    306 
    307           // Some blank panel
    308           JPanel blank_pane = new JPanel();
    309           blank_pane.setOpaque(false);
    310           JPanel edit_file_pane = new JPanel();
    311           edit_file_pane.setOpaque(false);
    312           JPanel edit_element_pane = new JPanel();
    313           edit_element_pane.setOpaque(false);
    314           JPanel edit_set_pane = new JPanel();
    315           edit_set_pane.setOpaque(false);
    316           // Connection
    317 
    318           add_attribute.addActionListener(add_or_edit_attribute_action_listener);
    319           add_element.addActionListener(add_element_action_listener);
    320           add_file.addActionListener(add_file_action_listener);
    321           add_set.addActionListener(add_set_action_listener);
    322           add_value.addActionListener(add_or_edit_value_action_listener);
    323           close.addActionListener(new CloseActionListener());
    324           edit_attribute.addActionListener(add_or_edit_attribute_action_listener);
    325           edit_value.addActionListener(add_or_edit_value_action_listener);
    326           remove_attribute.addActionListener(new RemoveAttributeActionListener());
    327           remove_element.addActionListener(new RemoveElementActionListener());
    328           remove_file.addActionListener(new RemoveFileActionListener());
    329           remove_set.addActionListener(new RemoveSetActionListener());
    330           remove_value.addActionListener(new RemoveValueActionListener());
    331           element_attributes.getSelectionModel().addListSelectionListener(new AttributesListSelectionListener(element_attributes));
    332           profile_attributes.getSelectionModel().addListSelectionListener(new AttributesListSelectionListener(profile_attributes));
    333           set_attributes.getSelectionModel().addListSelectionListener(new AttributesListSelectionListener(set_attributes));
    334           element_values.addTreeSelectionListener(new ElementValuesTreeSelectionListener());
    335           mds_tree.addTreeSelectionListener(new MDSTreeSelectionListener());
    336           // Layout
    337           mds_tree_pane.setLayout(new BorderLayout());
    338           mds_tree_pane.add(new JScrollPane(mds_tree), BorderLayout.CENTER);
    339          
    340           set_name_pane.setLayout(new BorderLayout());
    341           set_name_pane.add(set_name_label, BorderLayout.WEST);
    342           set_name_pane.add(set_name, BorderLayout.CENTER);
     65    extends JDialog {
     66    private AddElementActionListener add_element_action_listener = null;
     67    private AddFileActionListener add_file_action_listener = null;
     68    private AddSetActionListener add_set_action_listener = null;
     69    /** The class used to handle add or edit attribute actions has to be globally available so that we can dispose of its dialog properly. */
     70    private AddOrEditAttributeActionListener add_or_edit_attribute_action_listener = null;
     71    /** The class used to handle add or edit value actions has to be globally available so that we can dispose of its dialog properly. */
     72    private AddOrEditValueActionListener add_or_edit_value_action_listener = null;
     73    private boolean ignore = false;
     74    /** A card layout is used to switch between the two differing detail views. */
     75    private CardLayout card_layout = null;
     76    /** Um, the size of the screen I'd guess. */
     77    private Dimension screen_size = null;
     78    private ElementWrapper current_element = null;
     79    private GValueNode current_value_node = null;
     80    /** A reference to ourselves so our inner classes can dispose of us. */
     81    private MetadataEditorManager self = null;
     82    private int current_attribute = -1;
     83    private JButton add_attribute = null;
     84    private JButton add_element = null;
     85    private JButton add_file = null;
     86    private JButton add_set = null;
     87    private JButton add_value = null;
     88    private JButton close = null;
     89    private JButton edit_attribute = null;
     90    private JButton edit_value = null;
     91    private JButton remove_attribute = null;
     92    private JButton remove_element = null;
     93    private JButton remove_file = null;
     94    private JButton remove_set = null;
     95    private JButton remove_value = null;
     96    private JLabel element_name = null;
     97    private JLabel profile_name = null;
     98    private JLabel set_name = null;
     99    private JScrollPane element_attributes_scroll = null;
     100    private JScrollPane profile_attributes_scroll = null;
     101    private JScrollPane set_attributes_scroll = null;
     102    private JPanel details_pane = null;
     103    private MEMModel model = null;
     104    private MEMNode current_node = null;
     105    private MetadataSet current_set = null;
     106    private Object target = null;
     107    private SmarterTable element_attributes = null;
     108    private SmarterTable profile_attributes = null;
     109    private SmarterTable set_attributes = null;
     110    private SmarterTree element_values = null;
     111    /** A tree that represents the current metadata sets associated with this collection. */
     112    private SmarterTree mds_tree = null;
     113    private String current_collection_file = null;
     114    private String dialog_options[] = null;
     115    /** The default size of the editor dialog. */
     116    static final private Dimension ADD_ELEMENT_SIZE = new Dimension(400,125);
     117    static final private Dimension ADD_FILE_SIZE = new Dimension(400,125);
     118    static final private Dimension ADD_SET_SIZE = new Dimension(400,125);
     119    static final private Dimension ADD_OR_EDIT_ATTRIBUTE_SIZE = new Dimension(600,325);
     120    static final private Dimension ADD_OR_EDIT_VALUE_SIZE = new Dimension(600,440);
     121    static final private Dimension LABEL_SIZE = new Dimension(100,30);
     122    static final private Dimension SIZE = new Dimension(800,480);
     123    static final private String BLANK = "blank";
     124    static final private String ELEMENT = "element";
     125    static final private String PROFILE = "profile";
     126    static final private String SET = "set";
     127    /** Constructor. */
     128    public MetadataEditorManager() {
     129    super(Gatherer.g_man);
     130    this.dialog_options = new String[2];
     131    this.screen_size = Gatherer.config.screen_size;
     132    this.self = this;
     133
     134    dialog_options[0] = Gatherer.dictionary.get("General.OK");
     135    dialog_options[1] = Gatherer.dictionary.get("General.Cancel");
     136    // Creation
     137    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
     138    setModal(true);
     139    setSize(SIZE);
     140    setTitle(get("Title"));
     141    JPanel content_pane = (JPanel) getContentPane();
     142    content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
     143         
     144    JPanel upper_pane = new JPanel();
     145    upper_pane.setOpaque(false);
     146         
     147    model = new MEMModel();
     148
     149    JPanel mds_tree_pane = new JPanel();
     150    mds_tree_pane.setOpaque(false);
     151    mds_tree_pane.setPreferredSize(new Dimension(200,500));
     152    mds_tree = new SmarterTree(model);
     153    mds_tree.setCellRenderer(new MEMTreeCellRenderer());
     154    mds_tree.setRootVisible(false);
     155    mds_tree.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     156    mds_tree.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     157    mds_tree.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     158    mds_tree.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     159
     160    details_pane = new JPanel();
     161    details_pane.setOpaque(false);
     162    card_layout = new CardLayout();
     163         
     164    JPanel set_details_pane = new JPanel();
     165    set_details_pane.setOpaque(false);
     166         
     167    JPanel set_name_pane = new JPanel();
     168    set_name_pane.setOpaque(false);
     169    JLabel set_name_label = new JLabel(get("Name"));
     170    set_name_label.setOpaque(false);
     171    set_name_label.setPreferredSize(LABEL_SIZE);
     172    set_name = new JLabel();
     173    set_name.setBorder(BorderFactory.createLoweredBevelBorder());
     174    set_name.setOpaque(false);
     175         
     176    JPanel set_attributes_pane = new JPanel();
     177    set_attributes_pane.setOpaque(false);
     178    set_attributes_scroll = new JScrollPane();
     179    set_attributes_scroll.getViewport().setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     180    set_attributes_scroll.setOpaque(true);
     181    set_attributes = new SmarterTable();
     182    set_attributes.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
     183    set_attributes.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     184    set_attributes.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     185    set_attributes.setHeadingBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
     186    set_attributes.setHeadingForeground(Gatherer.config.getColor("coloring.collection_heading_foreground", false));
     187    set_attributes.setOpaque(false);
     188    set_attributes.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     189    set_attributes.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     190    set_attributes.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     191         
     192    JPanel element_details_pane = new JPanel();
     193    element_details_pane.setOpaque(false);
     194
     195    JPanel element_name_pane = new JPanel();
     196    element_name_pane.setOpaque(false);
     197    JLabel element_name_label = new JLabel(get("Name"));
     198    element_name_label.setOpaque(false);
     199    element_name_label.setPreferredSize(LABEL_SIZE);
     200    element_name = new JLabel();
     201    element_name.setBorder(BorderFactory.createLoweredBevelBorder());
     202    element_name.setOpaque(false);
     203         
     204    JPanel element_inner_pane = new JPanel();
     205    element_inner_pane.setOpaque(false);
     206         
     207    JPanel element_attributes_pane = new JPanel();
     208    element_attributes_pane.setOpaque(false);
     209    element_attributes_scroll = new JScrollPane();
     210    element_attributes_scroll.getViewport().setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     211    element_attributes_scroll.setOpaque(true);
     212    element_attributes = new SmarterTable();
     213    element_attributes.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
     214    element_attributes.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     215    element_attributes.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     216    element_attributes.setHeadingBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
     217    element_attributes.setHeadingForeground(Gatherer.config.getColor("coloring.collection_heading_foreground", false));
     218    element_attributes.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     219    element_attributes.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     220    element_attributes.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     221
     222    JPanel element_values_pane = new JPanel();
     223    element_values_pane.setOpaque(false);
     224    element_values = new SmarterTree();
     225    element_values.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     226    element_values.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     227    element_values.setRootVisible(false);
     228    element_values.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     229    element_values.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     230         
     231    JPanel profile_details_pane = new JPanel();
     232    profile_details_pane.setOpaque(false);
     233    JPanel profile_name_pane = new JPanel();
     234    profile_name_pane.setOpaque(false);
     235    JLabel profile_name_label = new JLabel(get("Name"));
     236    profile_name_label.setOpaque(false);
     237    profile_name_label.setPreferredSize(LABEL_SIZE);
     238    profile_name = new JLabel();
     239    profile_name.setBorder(BorderFactory.createLoweredBevelBorder());
     240    profile_name.setOpaque(false);
     241         
     242    JPanel profile_attributes_pane = new JPanel();
     243    profile_attributes_pane.setOpaque(false);
     244    profile_attributes_scroll = new JScrollPane();
     245    profile_attributes_scroll.getViewport().setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     246    profile_attributes_scroll.setOpaque(true);
     247    profile_attributes = new SmarterTable();
     248    profile_attributes.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
     249    profile_attributes.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     250    profile_attributes.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     251    profile_attributes.setHeadingBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
     252    profile_attributes.setHeadingForeground(Gatherer.config.getColor("coloring.collection_heading_foreground", false));
     253    profile_attributes.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     254    profile_attributes.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     255    profile_attributes.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     256
     257    JPanel button_pane = new JPanel();
     258    button_pane.setOpaque(false);
     259
     260    JPanel button_label_pane = new JPanel();
     261    button_label_pane.setOpaque(false);
     262    JLabel attribute_label = new JLabel(get("Attribute"));
     263    attribute_label.setOpaque(false);
     264    //attribute_label.setHorizontalAlignment(JLabel.CENTER);
     265    attribute_label.setPreferredSize(LABEL_SIZE);
     266    JLabel element_label = new JLabel(get("Element"));
     267    element_label.setOpaque(false);
     268    //element_label.setHorizontalAlignment(JLabel.CENTER);
     269    element_label.setPreferredSize(LABEL_SIZE);
     270    JLabel file_label = new JLabel(get("File"));
     271    file_label.setOpaque(false);
     272    //element_label.setHorizontalAlignment(JLabel.CENTER);
     273    file_label.setPreferredSize(LABEL_SIZE);
     274    JLabel set_label = new JLabel(get("Set"));
     275    set_label.setOpaque(false);
     276    //set_label.setHorizontalAlignment(JLabel.CENTER);
     277    set_label.setPreferredSize(LABEL_SIZE);
     278    JLabel value_label = new JLabel(get("Value"));
     279    value_label.setOpaque(false);
     280    //value_label.setHorizontalAlignment(JLabel.CENTER);
     281    value_label.setPreferredSize(LABEL_SIZE);
     282
     283    JPanel inner_button_pane = new JPanel();
     284    inner_button_pane.setOpaque(false);
     285    add_attribute = new JButton(get("Add"));
     286    add_element = new JButton(get("Add"));
     287    add_file = new JButton(get("Add"));
     288    add_set = new JButton(get("Add"));
     289    add_value = new JButton(get("Add"));
     290    edit_attribute = new JButton(get("Edit"));
     291    edit_value = new JButton(get("Edit"));
     292    remove_attribute = new JButton(get("Remove"));
     293    remove_element = new JButton(get("Remove"));
     294    remove_file = new JButton(get("Remove"));
     295    remove_set = new JButton(get("Remove"));
     296    remove_value = new JButton(get("Remove"));
     297    setControls(false, false, false, false, false, false, false, false, false, false, false, false);
     298
     299    close = new JButton(get("General.Close"));
     300
     301    add_element_action_listener = new AddElementActionListener();
     302    add_file_action_listener = new AddFileActionListener();
     303    add_set_action_listener = new AddSetActionListener();
     304    add_or_edit_attribute_action_listener = new AddOrEditAttributeActionListener();
     305    add_or_edit_value_action_listener = new AddOrEditValueActionListener();
     306
     307    // Some blank panel
     308    JPanel blank_pane = new JPanel();
     309    blank_pane.setOpaque(false);
     310    JPanel edit_file_pane = new JPanel();
     311    edit_file_pane.setOpaque(false);
     312    JPanel edit_element_pane = new JPanel();
     313    edit_element_pane.setOpaque(false);
     314    JPanel edit_set_pane = new JPanel();
     315    edit_set_pane.setOpaque(false);
     316    // Connection
     317
     318    add_attribute.addActionListener(add_or_edit_attribute_action_listener);
     319    add_element.addActionListener(add_element_action_listener);
     320    add_file.addActionListener(add_file_action_listener);
     321    add_set.addActionListener(add_set_action_listener);
     322    add_value.addActionListener(add_or_edit_value_action_listener);
     323    close.addActionListener(new CloseActionListener());
     324    edit_attribute.addActionListener(add_or_edit_attribute_action_listener);
     325    edit_value.addActionListener(add_or_edit_value_action_listener);
     326    remove_attribute.addActionListener(new RemoveAttributeActionListener());
     327    remove_element.addActionListener(new RemoveElementActionListener());
     328    remove_file.addActionListener(new RemoveFileActionListener());
     329    remove_set.addActionListener(new RemoveSetActionListener());
     330    remove_value.addActionListener(new RemoveValueActionListener());
     331    element_attributes.getSelectionModel().addListSelectionListener(new AttributesListSelectionListener(element_attributes));
     332    profile_attributes.getSelectionModel().addListSelectionListener(new AttributesListSelectionListener(profile_attributes));
     333    set_attributes.getSelectionModel().addListSelectionListener(new AttributesListSelectionListener(set_attributes));
     334    element_values.addTreeSelectionListener(new ElementValuesTreeSelectionListener());
     335    mds_tree.addTreeSelectionListener(new MDSTreeSelectionListener());
     336    // Layout
     337    mds_tree_pane.setLayout(new BorderLayout());
     338    mds_tree_pane.add(new JScrollPane(mds_tree), BorderLayout.CENTER);
     339         
     340    set_name_pane.setLayout(new BorderLayout());
     341    set_name_pane.add(set_name_label, BorderLayout.WEST);
     342    set_name_pane.add(set_name, BorderLayout.CENTER);
    343343               
    344           set_attributes_scroll.setViewportView(set_attributes);
    345 
    346           set_attributes_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Attributes")), BorderFactory.createEmptyBorder(2,2,2,2)));
    347           set_attributes_pane.setLayout(new BorderLayout());
    348           set_attributes_pane.add(set_attributes_scroll, BorderLayout.CENTER);
     344    set_attributes_scroll.setViewportView(set_attributes);
     345
     346    set_attributes_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Attributes")), BorderFactory.createEmptyBorder(2,2,2,2)));
     347    set_attributes_pane.setLayout(new BorderLayout());
     348    set_attributes_pane.add(set_attributes_scroll, BorderLayout.CENTER);
    349349       
    350           set_details_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Set_Details")), BorderFactory.createEmptyBorder(2,2,2,2)));
    351           set_details_pane.setLayout(new BorderLayout());
    352           //set_details_pane.add(set_name_pane, BorderLayout.NORTH);
    353           set_details_pane.add(set_attributes_pane, BorderLayout.CENTER);
    354          
    355           element_name_pane.setLayout(new BorderLayout());
    356           element_name_pane.add(element_name_label, BorderLayout.WEST);
    357           element_name_pane.add(element_name, BorderLayout.CENTER);
     350    set_details_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Set_Details")), BorderFactory.createEmptyBorder(2,2,2,2)));
     351    set_details_pane.setLayout(new BorderLayout());
     352    //set_details_pane.add(set_name_pane, BorderLayout.NORTH);
     353    set_details_pane.add(set_attributes_pane, BorderLayout.CENTER);
     354         
     355    element_name_pane.setLayout(new BorderLayout());
     356    element_name_pane.add(element_name_label, BorderLayout.WEST);
     357    element_name_pane.add(element_name, BorderLayout.CENTER);
    358358               
    359           element_attributes_scroll.setViewportView(element_attributes);
    360 
    361           element_attributes_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Attributes")), BorderFactory.createEmptyBorder(2,2,2,2)));
    362           element_attributes_pane.setLayout(new BorderLayout());
    363           element_attributes_pane.add(element_attributes_scroll, BorderLayout.CENTER);
    364          
    365           element_values_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Values")), BorderFactory.createEmptyBorder(2,2,2,2)));
    366           element_values_pane.setLayout(new BorderLayout());
    367           element_values_pane.add(new JScrollPane(element_values), BorderLayout.CENTER);
    368          
    369           element_inner_pane.setLayout(new GridLayout(2,1));
    370           element_inner_pane.add(element_attributes_pane);
    371           element_inner_pane.add(element_values_pane);
    372          
    373           element_details_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Element_Details")), BorderFactory.createEmptyBorder(2,2,2,2)));
    374           element_details_pane.setLayout(new BorderLayout());
    375           //element_details_pane.add(element_name_pane, BorderLayout.NORTH);
    376           element_details_pane.add(element_inner_pane, BorderLayout.CENTER);
    377 
    378           profile_name_pane.setLayout(new BorderLayout());
    379           profile_name_pane.add(profile_name_label, BorderLayout.WEST);
    380           profile_name_pane.add(profile_name, BorderLayout.CENTER);
     359    element_attributes_scroll.setViewportView(element_attributes);
     360
     361    element_attributes_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Attributes")), BorderFactory.createEmptyBorder(2,2,2,2)));
     362    element_attributes_pane.setLayout(new BorderLayout());
     363    element_attributes_pane.add(element_attributes_scroll, BorderLayout.CENTER);
     364         
     365    element_values_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Values")), BorderFactory.createEmptyBorder(2,2,2,2)));
     366    element_values_pane.setLayout(new BorderLayout());
     367    element_values_pane.add(new JScrollPane(element_values), BorderLayout.CENTER);
     368         
     369    element_inner_pane.setLayout(new GridLayout(2,1));
     370    element_inner_pane.add(element_attributes_pane);
     371    element_inner_pane.add(element_values_pane);
     372         
     373    element_details_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Element_Details")), BorderFactory.createEmptyBorder(2,2,2,2)));
     374    element_details_pane.setLayout(new BorderLayout());
     375    //element_details_pane.add(element_name_pane, BorderLayout.NORTH);
     376    element_details_pane.add(element_inner_pane, BorderLayout.CENTER);
     377
     378    profile_name_pane.setLayout(new BorderLayout());
     379    profile_name_pane.add(profile_name_label, BorderLayout.WEST);
     380    profile_name_pane.add(profile_name, BorderLayout.CENTER);
    381381               
    382           profile_attributes_scroll.setViewportView(profile_attributes);
    383 
    384           profile_attributes_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Profiles")), BorderFactory.createEmptyBorder(2,2,2,2)));
    385           profile_attributes_pane.setLayout(new BorderLayout());
    386           profile_attributes_pane.add(profile_attributes_scroll, BorderLayout.CENTER);
     382    profile_attributes_scroll.setViewportView(profile_attributes);
     383
     384    profile_attributes_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Profiles")), BorderFactory.createEmptyBorder(2,2,2,2)));
     385    profile_attributes_pane.setLayout(new BorderLayout());
     386    profile_attributes_pane.add(profile_attributes_scroll, BorderLayout.CENTER);
    387387       
    388           profile_details_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Profile_Details")), BorderFactory.createEmptyBorder(2,2,2,2)));
    389           profile_details_pane.setLayout(new BorderLayout());
    390           //profile_details_pane.add(profile_name_pane, BorderLayout.NORTH);
    391           profile_details_pane.add(profile_attributes_pane, BorderLayout.CENTER);
    392          
    393           details_pane.setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
    394           details_pane.setLayout(card_layout);
    395           details_pane.add(blank_pane, BLANK);
    396           details_pane.add(set_details_pane, SET);
    397           details_pane.add(element_details_pane, ELEMENT);
    398           details_pane.add(profile_details_pane, PROFILE);
    399 
    400           upper_pane.setLayout(new BorderLayout());
    401           upper_pane.add(mds_tree_pane, BorderLayout.WEST);
    402           upper_pane.add(details_pane, BorderLayout.CENTER);
    403          
    404           button_label_pane.setLayout(new GridLayout(5,1,0,2));
    405           button_label_pane.add(set_label);
    406           button_label_pane.add(file_label);
    407           button_label_pane.add(element_label);
    408           button_label_pane.add(attribute_label);
    409           button_label_pane.add(value_label);
    410 
    411           inner_button_pane.setLayout(new GridLayout(5,3,0,2));     
    412           inner_button_pane.add(add_set);
    413           inner_button_pane.add(edit_set_pane);
    414           inner_button_pane.add(remove_set);
    415           inner_button_pane.add(add_file);
    416           inner_button_pane.add(edit_file_pane);
    417           inner_button_pane.add(remove_file);
    418           inner_button_pane.add(add_element);
    419           inner_button_pane.add(edit_element_pane);
    420           inner_button_pane.add(remove_element);
    421           inner_button_pane.add(add_attribute);
    422           inner_button_pane.add(edit_attribute);
    423           inner_button_pane.add(remove_attribute);
    424           inner_button_pane.add(add_value);
    425           inner_button_pane.add(edit_value);
    426           inner_button_pane.add(remove_value);
    427          
    428           button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
    429           button_pane.setLayout(new BorderLayout());
    430           button_pane.add(button_label_pane, BorderLayout.WEST);
    431           button_pane.add(inner_button_pane, BorderLayout.CENTER);
    432           button_pane.add(close, BorderLayout.EAST);
    433 
    434           content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    435           content_pane.setLayout(new BorderLayout());
    436           content_pane.add(upper_pane, BorderLayout.CENTER);
    437           content_pane.add(button_pane, BorderLayout.SOUTH);
    438 
    439           // Display
    440           setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
    441           show();
    442     }
     388    profile_details_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Profile_Details")), BorderFactory.createEmptyBorder(2,2,2,2)));
     389    profile_details_pane.setLayout(new BorderLayout());
     390    //profile_details_pane.add(profile_name_pane, BorderLayout.NORTH);
     391    profile_details_pane.add(profile_attributes_pane, BorderLayout.CENTER);
     392         
     393    details_pane.setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
     394    details_pane.setLayout(card_layout);
     395    details_pane.add(blank_pane, BLANK);
     396    details_pane.add(set_details_pane, SET);
     397    details_pane.add(element_details_pane, ELEMENT);
     398    details_pane.add(profile_details_pane, PROFILE);
     399
     400    upper_pane.setLayout(new BorderLayout());
     401    upper_pane.add(mds_tree_pane, BorderLayout.WEST);
     402    upper_pane.add(details_pane, BorderLayout.CENTER);
     403         
     404    button_label_pane.setLayout(new GridLayout(5,1,0,2));
     405    button_label_pane.add(set_label);
     406    button_label_pane.add(file_label);
     407    button_label_pane.add(element_label);
     408    button_label_pane.add(attribute_label);
     409    button_label_pane.add(value_label);
     410
     411    inner_button_pane.setLayout(new GridLayout(5,3,0,2));       
     412    inner_button_pane.add(add_set);
     413    inner_button_pane.add(edit_set_pane);
     414    inner_button_pane.add(remove_set);
     415    inner_button_pane.add(add_file);
     416    inner_button_pane.add(edit_file_pane);
     417    inner_button_pane.add(remove_file);
     418    inner_button_pane.add(add_element);
     419    inner_button_pane.add(edit_element_pane);
     420    inner_button_pane.add(remove_element);
     421    inner_button_pane.add(add_attribute);
     422    inner_button_pane.add(edit_attribute);
     423    inner_button_pane.add(remove_attribute);
     424    inner_button_pane.add(add_value);
     425    inner_button_pane.add(edit_value);
     426    inner_button_pane.add(remove_value);
     427         
     428    button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
     429    button_pane.setLayout(new BorderLayout());
     430    button_pane.add(button_label_pane, BorderLayout.WEST);
     431    button_pane.add(inner_button_pane, BorderLayout.CENTER);
     432    button_pane.add(close, BorderLayout.EAST);
     433
     434    content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     435    content_pane.setLayout(new BorderLayout());
     436    content_pane.add(upper_pane, BorderLayout.CENTER);
     437    content_pane.add(button_pane, BorderLayout.SOUTH);
     438
     439    // Display
     440    setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
     441    show();
     442    }
    443443     
    444     public void dispose() {
    445           // Destructor
    446           card_layout = null;
    447           screen_size = null;
    448           self = null;
    449           add_attribute = null;
    450           add_element = null;
    451           add_file = null;
    452           add_set = null;
    453           add_value = null;
    454           close = null;
    455           edit_attribute = null;
    456           edit_value = null;
    457           remove_attribute = null;
    458           remove_element = null;
    459           remove_file = null;
    460           remove_set = null;
    461           remove_value = null;
    462           details_pane = null;
    463           element_attributes = null;
    464           set_attributes = null;
    465           element_name = null;
    466           set_name = null;
    467           element_values = null;
    468           mds_tree = null;
    469           target = null;
    470           // Dispose of inner dialogs
    471           add_element_action_listener.dispose();
    472           add_or_edit_attribute_action_listener.dispose();
    473           add_or_edit_value_action_listener.dispose();
    474           super.dispose();
    475     }
    476 
    477     private String get(String key) {
    478           return get(key, (String[])null);
    479     }
     444    public void dispose() {
     445    // Destructor
     446    card_layout = null;
     447    screen_size = null;
     448    self = null;
     449    add_attribute = null;
     450    add_element = null;
     451    add_file = null;
     452    add_set = null;
     453    add_value = null;
     454    close = null;
     455    edit_attribute = null;
     456    edit_value = null;
     457    remove_attribute = null;
     458    remove_element = null;
     459    remove_file = null;
     460    remove_set = null;
     461    remove_value = null;
     462    details_pane = null;
     463    element_attributes = null;
     464    set_attributes = null;
     465    element_name = null;
     466    set_name = null;
     467    element_values = null;
     468    mds_tree = null;
     469    target = null;
     470    // Dispose of inner dialogs
     471    add_element_action_listener.dispose();
     472    add_or_edit_attribute_action_listener.dispose();
     473    add_or_edit_value_action_listener.dispose();
     474    super.dispose();
     475    }
     476
     477    private String get(String key) {
     478    return get(key, (String[])null);
     479    }
    480480     
    481     private String get(String key, String arg) {
    482           String[] args = new String[1];
    483           args[0] = arg;
    484           return get(key, args);
    485     }
    486 
    487     private String get(String key, String[] args) {
    488           if(key.indexOf(".") == -1) {
    489                 key = "MEM." + key;
    490           }
    491           return Gatherer.dictionary.get(key, args);
    492     }
     481    private String get(String key, String arg) {
     482    String[] args = new String[1];
     483    args[0] = arg;
     484    return get(key, args);
     485    }
     486
     487    private String get(String key, String[] args) {
     488    if(key.indexOf(".") == -1) {
     489        key = "MEM." + key;
     490    }
     491    return Gatherer.dictionary.get(key, args);
     492    }
    493493     
    494     private void setControls(boolean a_s, boolean r_s, boolean a_f, boolean r_f, boolean a_e, boolean r_e, boolean a_a, boolean e_a, boolean r_a, boolean a_v, boolean e_v, boolean r_v) {
    495           add_attribute.setEnabled(a_a);
    496           add_element.setEnabled(a_e);
    497           add_file.setEnabled(true); // Always true
    498           add_set.setEnabled(true); // Always true
    499           add_value.setEnabled(a_v);
    500           edit_attribute.setEnabled(e_a);
    501           edit_value.setEnabled(e_v);
    502           remove_attribute.setEnabled(r_a);
    503           remove_element.setEnabled(r_e);
    504           remove_file.setEnabled(r_f);
    505           remove_set.setEnabled(r_s);
    506           remove_value.setEnabled(r_v);
    507     }
    508 
    509     private class AddOrEditAttributeActionListener
    510           extends JDialog
    511           implements ActionListener {
    512           private boolean add_type = true;
    513           private ComboArea value = null;
    514           private JButton cancel_button = null;
    515           private JButton ok_button = null;
    516           private JComboBox language_box = null;
    517           private GComboBox name = null;
    518           private HashMap name_to_values = null;
    519           private JLabel target = null;
    520           /** Constructor. */
    521           public AddOrEditAttributeActionListener() {
    522                 super(self);
    523                 setModal(true);
    524                 setSize(ADD_OR_EDIT_ATTRIBUTE_SIZE);
    525                 name_to_values = new HashMap();
     494    private void setControls(boolean a_s, boolean r_s, boolean a_f, boolean r_f, boolean a_e, boolean r_e, boolean a_a, boolean e_a, boolean r_a, boolean a_v, boolean e_v, boolean r_v) {
     495    add_attribute.setEnabled(a_a);
     496    add_element.setEnabled(a_e);
     497    add_file.setEnabled(true); // Always true
     498    add_set.setEnabled(true); // Always true
     499    add_value.setEnabled(a_v);
     500    edit_attribute.setEnabled(e_a);
     501    edit_value.setEnabled(e_v);
     502    remove_attribute.setEnabled(r_a);
     503    remove_element.setEnabled(r_e);
     504    remove_file.setEnabled(r_f);
     505    remove_set.setEnabled(r_s);
     506    remove_value.setEnabled(r_v);
     507    }
     508
     509    private class AddOrEditAttributeActionListener
     510    extends JDialog
     511    implements ActionListener {
     512    private boolean add_type = true;
     513    private ComboArea value = null;
     514    private JButton cancel_button = null;
     515    private JButton ok_button = null;
     516    private JComboBox language_box = null;
     517    private GComboBox name = null;
     518    private HashMap name_to_values = null;
     519    private JLabel target = null;
     520    /** Constructor. */
     521    public AddOrEditAttributeActionListener() {
     522        super(self);
     523        setModal(true);
     524        setSize(ADD_OR_EDIT_ATTRIBUTE_SIZE);
     525        name_to_values = new HashMap();
    526526                // Creation
    527                 JPanel content_pane = (JPanel) getContentPane();
    528                 content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
    529                 JPanel upper_pane = new JPanel();
    530                 upper_pane.setOpaque(false);
    531 
    532                 JPanel target_pane = new JPanel();
    533                 target_pane.setOpaque(false);
    534                 JLabel target_label = new JLabel(get("Target"));
    535                 target_label.setOpaque(false);
    536                 target_label.setPreferredSize(LABEL_SIZE);
    537                 target = new JLabel();
    538                 target.setOpaque(false);
    539 
    540 
    541                 JPanel name_pane = new JPanel();
    542                 name_pane.setOpaque(false);
    543                 JLabel name_label = new JLabel(get("Name"));
    544                 name_label.setOpaque(false);
    545                 name_label.setPreferredSize(LABEL_SIZE);
    546                 name = new GComboBox();
    547                 name.setEditable(true);
    548 
    549                 JPanel language_pane = new JPanel();
    550                 language_pane.setOpaque(false);
    551                 JLabel language_label = new JLabel(get("Language"));
    552                 language_label.setOpaque(false);
    553                 language_label.setPreferredSize(LABEL_SIZE);
    554                 language_box = new JComboBox(Gatherer.g_man.config_pane.getLanguageCodes().toArray());
    555 
    556                 JPanel center_pane = new JPanel();
    557                 center_pane.setOpaque(false);
    558                 value = new ComboArea(get("Values"), LABEL_SIZE);
    559                 value.setOpaque(false);
    560                 JTextArea v_text_area = (JTextArea) value.getTextComponent();
    561                 v_text_area.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    562                 v_text_area.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    563                 v_text_area.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    564                 v_text_area.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    565                 JPanel button_pane = new JPanel();
    566                 button_pane.setOpaque(false);
    567                 ok_button = new JButton(get("General.OK"));
    568                 cancel_button = new JButton(get("General.Cancel"));
     527        JPanel content_pane = (JPanel) getContentPane();
     528        content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
     529        JPanel upper_pane = new JPanel();
     530        upper_pane.setOpaque(false);
     531
     532        JPanel target_pane = new JPanel();
     533        target_pane.setOpaque(false);
     534        JLabel target_label = new JLabel(get("Target"));
     535        target_label.setOpaque(false);
     536        target_label.setPreferredSize(LABEL_SIZE);
     537        target = new JLabel();
     538        target.setOpaque(false);
     539
     540
     541        JPanel name_pane = new JPanel();
     542        name_pane.setOpaque(false);
     543        JLabel name_label = new JLabel(get("Name"));
     544        name_label.setOpaque(false);
     545        name_label.setPreferredSize(LABEL_SIZE);
     546        name = new GComboBox();
     547        name.setEditable(true);
     548
     549        JPanel language_pane = new JPanel();
     550        language_pane.setOpaque(false);
     551        JLabel language_label = new JLabel(get("Language"));
     552        language_label.setOpaque(false);
     553        language_label.setPreferredSize(LABEL_SIZE);
     554        language_box = new JComboBox(Gatherer.g_man.config_pane.getLanguageCodes().toArray());
     555
     556        JPanel center_pane = new JPanel();
     557        center_pane.setOpaque(false);
     558        value = new ComboArea(get("Values"), LABEL_SIZE);
     559        value.setOpaque(false);
     560        JTextArea v_text_area = (JTextArea) value.getTextComponent();
     561        v_text_area.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     562        v_text_area.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     563        v_text_area.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     564        v_text_area.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     565        JPanel button_pane = new JPanel();
     566        button_pane.setOpaque(false);
     567        ok_button = new JButton(get("General.OK"));
     568        cancel_button = new JButton(get("General.Cancel"));
    569569                // Connection
    570                 TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button);
    571                 cancel_button.addActionListener(this);
    572                 name.addActionListener(this);
    573                 ok_button.addActionListener(this);
    574                 ok_button_enabler.add((JTextComponent)name.getEditor()); // Assuming the default editor is a JTextField!
     570        TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button);
     571        cancel_button.addActionListener(this);
     572        name.addActionListener(this);
     573        ok_button.addActionListener(this);
     574        ok_button_enabler.add((JTextComponent)name.getEditor()); // Assuming the default editor is a JTextField!
    575575                // Layout
    576                 target_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    577                 target_pane.setLayout(new BorderLayout());
    578                 target_pane.add(target_label, BorderLayout.WEST);
    579                 target_pane.add(target, BorderLayout.CENTER);
     576        target_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
     577        target_pane.setLayout(new BorderLayout());
     578        target_pane.add(target_label, BorderLayout.WEST);
     579        target_pane.add(target, BorderLayout.CENTER);
    580580               
    581                 language_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    582                 language_pane.setLayout(new BorderLayout());
    583                 language_pane.add(language_label, BorderLayout.WEST);
    584                 language_pane.add(language_box, BorderLayout.CENTER);
    585 
    586                 name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    587                 name_pane.setLayout(new BorderLayout());
    588                 name_pane.add(name_label, BorderLayout.WEST);
    589                 name_pane.add(name, BorderLayout.CENTER);
    590 
    591                 upper_pane.setLayout(new GridLayout(3,1));
    592                 upper_pane.add(target_pane);
    593                 upper_pane.add(name_pane);
    594                 upper_pane.add(language_pane);
    595 
    596                 value.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    597 
    598                 button_pane.setLayout(new GridLayout(1,2,5,0));
    599                 button_pane.add(ok_button);
    600                 button_pane.add(cancel_button);
    601 
    602                 center_pane.setLayout(new BorderLayout());
    603                 center_pane.add(value, BorderLayout.CENTER);
    604                 center_pane.add(button_pane, BorderLayout.SOUTH);
    605 
    606                 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    607                 content_pane.setLayout(new BorderLayout());
    608                 content_pane.add(upper_pane, BorderLayout.NORTH);
    609                 content_pane.add(center_pane, BorderLayout.CENTER);
    610 
    611                 setLocation((Gatherer.config.screen_size.width - ADD_OR_EDIT_ATTRIBUTE_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_OR_EDIT_ATTRIBUTE_SIZE.height) / 2);
    612           }
    613           /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to add a new attribute to the selected set or element.
    614             * @param event An <strong>ActionEvent</strong> containing information about the event.
    615             */
    616           public void actionPerformed(ActionEvent event) {
    617                 Object source = event.getSource();
    618                 if(source == ok_button) {
    619                      boolean cont = true;
    620                      // Now we add the new or improved values, removing old values if this is an edit.
    621                      // Depends on what we are adding the attribute to.
    622                      if(current_collection_file != null) {
    623                           String name_str = name.getSelectedItem().toString();
    624                           String value_str = value.getText();
    625                           AttributeTableModel model = (AttributeTableModel) profile_attributes.getModel();
    626                           // Remove the existing one if this is an edit.
    627                           if(!add_type && current_attribute != -1) {
    628                                 String old_source = (String) profile_attributes.getValueAt(current_attribute, 0);
    629                                 Gatherer.c_man.msm.profiler.removeAction(current_collection_file, old_source);
    630                                 old_source = null;
    631                                 model.removeRow(current_attribute);
    632                           }
    633                           // First thing we check from profiles is that there isn't already an entry for this name.
    634                           if(!model.contains(name_str, 0)) {
    635                                 // Add profile
    636                                 Gatherer.c_man.msm.profiler.addAction(current_collection_file, name_str, value_str);
    637                                 // Update attribute table.
    638                                 model.add(new Attribute(name_str, value_str));
    639                           }
    640                           else {
    641                                 // Show an error message and do not proceed.
    642                                 cont = false;
    643                                 JOptionPane.showMessageDialog(self, get("Attribute_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
    644                           }
    645                           model = null;
    646                           value_str = null;
    647                           name_str = null;
    648                      }
    649                      else if(current_element != null) {
    650                           // Add the attribute, even if one of the same name already exists. Maybe one day someone would like to enforce the occurance rules but not me and not today.
    651                           String name_str = name.getSelectedItem().toString();
    652                           String language_code = ((Language)language_box.getSelectedItem()).getCode();
    653                           String value_str = value.getText();
    654                           AttributeTableModel model = (AttributeTableModel) element_attributes.getModel();
    655                           // Remove the existing one if this is an edit.
    656                           if(!add_type && current_attribute != -1) {
    657                                 model.removeRow(current_attribute);
    658                           }
    659                           // Add attribute
    660                           current_element.addAttribute(name_str, language_code, value_str);
    661                           // Update the attribute table
    662                           model.add(new Attribute(name_str, language_code, value_str));
    663                           model = null;
    664                           value_str = null;
    665                           language_code = null;
    666                           name_str = null;
    667                      }
    668                      else if(current_set != null) {
    669                           String name_str = name.getSelectedItem().toString();
    670                           String value_str = value.getText();                       
    671                           // First thing we check from profiles is that there isn't already an entry for this name.
    672                           AttributeTableModel model = (AttributeTableModel) set_attributes.getModel();
    673                           // Remove the existing one if this is an edit.
    674                           if(!add_type && current_attribute != -1) {
    675                                 model.removeRow(current_attribute);
    676                           }
    677                           // First thing we check from profiles is that there isn't already an entry for this name.
    678                           if(!model.contains(name_str, 0)) {
    679                                 // Add profile
    680                                 current_set.addAttribute(name_str, value_str);
    681                                 // Update attribute table.
    682                                 model.add(new Attribute(name_str, value_str));
    683                           }
    684                           // Otherwise provide an error message and do not proceed.
    685                           else {
    686                                 cont = false;
    687                                 JOptionPane.showMessageDialog(self, get("Attribute_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
    688                           }
    689                           model = null;
    690                           value_str = null;
    691                           name_str = null;
    692                      }
    693                      // Hide dialog if we are allowed to continue.
    694                      if(cont) {
    695                           hide();
    696                      }
    697                 }
    698                 else if(source == cancel_button) {
    699                      // Hide dialog
    700                      hide();
    701                 }
    702                 else if(source == name) {
    703                      Object object = name.getSelectedItem();
    704                      if(object != null) {
    705                           java.util.List values = (java.util.List) name_to_values.get(object.toString());
    706                           if(values == null) { // Only for collection files, otherwise values != null && values.size() == 0
    707                                 values = Gatherer.c_man.msm.getElements();
    708                           }
    709                           value.clear();
    710                           for(int i = 0; i < values.size(); i++) {
    711                                 value.add(values.get(i));
    712                           }
    713                      }
    714                 }
    715                 else {
    716                      name_to_values.clear();
    717                      name.clear();
    718                      value.clear();
    719                      value.setText("");
    720                      // Build the correct name_to_values mapping
    721                      // Attributes are slightly tricky as they can come from several sources. Has a collection file been selected...
    722                      if(current_collection_file != null) {
    723                           target.setText(current_collection_file);
    724                           // Name is empty in this one, however values must be the current elements in the collection.                       
    725                           language_box.setEnabled(false);
    726                      }
    727                      // or has an element been selected
    728                      else if(current_element != null) {
    729                           target.setText(current_element.toString());
    730                           // Develop a model for name based on known attributes from all other elements.
    731                           java.util.List elements = Gatherer.c_man.msm.getElements();
    732                           for(int i = 0; i < elements.size(); i++) {
    733                                 ElementWrapper element = (ElementWrapper) elements.get(i);
    734                                 TreeSet attributes = element.getAttributes();
    735                                 for(Iterator attribute_iterator = attributes.iterator(); attribute_iterator.hasNext(); ) {
    736                                     Attribute attribute = (Attribute) attribute_iterator.next();
    737                                     java.util.List values = (java.util.List) name_to_values.get(attribute.name);
    738                                     if(values == null) {
    739                                           values = new ArrayList();
    740                                           name_to_values.put(attribute.name, values);
    741                                     }
    742                                     if(!values.contains(attribute.value)) {
    743                                           values.add(attribute.value);
    744                                     }
    745                                     values = null;
    746                                     attribute = null;
    747                                 }
    748                                 attributes = null;
    749                                 element = null;
    750                           }
    751                           elements = null;
    752                           language_box.setEnabled(true);
    753                      }
    754                      else if(current_set != null) {
    755                           target.setText(current_set.toString());
    756                           // Develop a model for name based on known attributes from all other metadata sets. At the same time build a hashmap mapping attribute name to lists of values.
    757                           java.util.List sets = Gatherer.c_man.msm.getSets();
    758                           for(int i = 0; i < sets.size(); i++) {
    759                                 MetadataSet set = (MetadataSet) sets.get(i);
    760                                 NamedNodeMap attributes = set.getAttributes();
    761                                 for(int j = 0; j < attributes.getLength(); j++) {
    762                                     Attr attribute = (Attr) attributes.item(j);
    763                                     String name_str = attribute.getName();
    764                                     String value_str = attribute.getValue();
    765                                     java.util.List values = (java.util.List) name_to_values.get(name_str);
    766                                     if(values == null) {
    767                                           values = new ArrayList();
    768                                           name_to_values.put(name_str, values);
    769                                     }
    770                                     if(!values.contains(value_str)) {
    771                                           values.add(value_str);
    772                                     }
    773                                     values = null;
    774                                     value_str = null;
    775                                     name_str = null;
    776                                     attribute = null;
    777                                 }
    778                                 attributes = null;
    779                                 set = null;
    780                                 language_box.setEnabled(false);
    781                           }
    782                           sets = null;
    783                           // If this is an add remove all the attributes already present in the current set.
    784                           if(source == add_attribute) {
    785                                 name.setEnabled(true);
    786                                 NamedNodeMap attributes = current_set.getAttributes();
    787                                 for(int i = 0; i < attributes.getLength(); i++) {
    788                                     Attr attribute = (Attr) attributes.item(i);
    789                                     String name_str = attribute.getName();
    790                                     name_to_values.remove(name_str);
    791                                     name_str = null;
    792                                     attribute = null;
    793                                 }
    794                                 attributes = null;
    795                           }
    796                      }
    797                      // Otherwise we actually disable the name combobox
    798                      else {
    799                           name.setEnabled(false);
    800                      }
    801                      // Now name_to_values should contain a list of unique attribute names each mapping to a list of attribute values.
    802                      for(Iterator name_iterator = name_to_values.keySet().iterator(); name_iterator.hasNext(); ) {
    803                           name.add(name_iterator.next());
    804                      }
    805                      // Now pritty up the dialog depending on the action type
    806                      if(source == add_attribute) {
    807                           add_type = true;
    808                           setTitle(get("AddAttribute"));
    809                           if(current_collection_file != null) {
    810                                 // Name is empty in this one, however values must be the current elements in the collection.   
    811                                 java.util.List values = Gatherer.c_man.msm.getElements();
    812                                 for(int i = 0; i < values.size(); i++) {
    813                                     value.add(new NameElementWrapperEntry(values.get(i)));
    814                                 }
    815                                 values = null;
    816                           }
    817                           show();
    818                      }
    819                      else if(current_attribute != -1) {
    820                           add_type = false;
    821                           setTitle(get("EditAttribute"));
    822                           String name_str = (String) profile_attributes.getValueAt(current_attribute, 0);
    823                           String value_str = (String) profile_attributes.getValueAt(current_attribute, 1);
    824                           // Retrieve the appropriate value model
    825                           java.util.List values = (java.util.List) name_to_values.get(name_str);
    826                           // Only possible for collection file selections.
    827                           if(values == null) {
    828                                 values = Gatherer.c_man.msm.getElements();
    829                           }
    830                           name.setSelectedItem(name_str);
    831                           name_str = null;
    832                           for(int i = 0; i < values.size(); i++) {
    833                                 value.add(new NameElementWrapperEntry(values.get(i)));
    834                           }
    835                           values = null;
    836                           value.setSelectedItem(value_str);
    837                           value_str = null;
    838                           show();
    839                      }
    840                 }
    841                 source = null;
    842           }
    843          
    844           public void dispose() {
    845                 cancel_button = null;
    846                 name = null;
    847                 name_to_values = null;
    848                 ok_button = null;
    849                 target = null;
    850                 value = null;
    851           }
    852     }
     581        language_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
     582        language_pane.setLayout(new BorderLayout());
     583        language_pane.add(language_label, BorderLayout.WEST);
     584        language_pane.add(language_box, BorderLayout.CENTER);
     585
     586        name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
     587        name_pane.setLayout(new BorderLayout());
     588        name_pane.add(name_label, BorderLayout.WEST);
     589        name_pane.add(name, BorderLayout.CENTER);
     590
     591        upper_pane.setLayout(new GridLayout(3,1));
     592        upper_pane.add(target_pane);
     593        upper_pane.add(name_pane);
     594        upper_pane.add(language_pane);
     595
     596        value.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
     597
     598        button_pane.setLayout(new GridLayout(1,2,5,0));
     599        button_pane.add(ok_button);
     600        button_pane.add(cancel_button);
     601
     602        center_pane.setLayout(new BorderLayout());
     603        center_pane.add(value, BorderLayout.CENTER);
     604        center_pane.add(button_pane, BorderLayout.SOUTH);
     605
     606        content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     607        content_pane.setLayout(new BorderLayout());
     608        content_pane.add(upper_pane, BorderLayout.NORTH);
     609        content_pane.add(center_pane, BorderLayout.CENTER);
     610
     611        setLocation((Gatherer.config.screen_size.width - ADD_OR_EDIT_ATTRIBUTE_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_OR_EDIT_ATTRIBUTE_SIZE.height) / 2);
     612    }
     613    /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to add a new attribute to the selected set or element.
     614     * @param event An <strong>ActionEvent</strong> containing information about the event.
     615     */
     616    public void actionPerformed(ActionEvent event) {
     617        Object source = event.getSource();
     618        if(source == ok_button) {
     619        boolean cont = true;
     620        // Now we add the new or improved values, removing old values if this is an edit.
     621        // Depends on what we are adding the attribute to.
     622        if(current_collection_file != null) {
     623            String name_str = name.getSelectedItem().toString();
     624            String value_str = value.getText();
     625            AttributeTableModel model = (AttributeTableModel) profile_attributes.getModel();
     626            // Remove the existing one if this is an edit.
     627            if(!add_type && current_attribute != -1) {
     628            String old_source = (String) profile_attributes.getValueAt(current_attribute, 0);
     629            Gatherer.c_man.msm.profiler.removeAction(current_collection_file, old_source);
     630            old_source = null;
     631            model.removeRow(current_attribute);
     632            }
     633            // First thing we check from profiles is that there isn't already an entry for this name.
     634            if(!model.contains(name_str, 0)) {
     635            // Add profile
     636            Gatherer.c_man.msm.profiler.addAction(current_collection_file, name_str, value_str);
     637            // Update attribute table.
     638            model.add(new Attribute(name_str, value_str));
     639            }
     640            else {
     641            // Show an error message and do not proceed.
     642            cont = false;
     643            JOptionPane.showMessageDialog(self, get("Attribute_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
     644            }
     645            model = null;
     646            value_str = null;
     647            name_str = null;
     648        }
     649        else if(current_element != null) {
     650            // Add the attribute, even if one of the same name already exists. Maybe one day someone would like to enforce the occurance rules but not me and not today.
     651            String name_str = name.getSelectedItem().toString();
     652            String language_code = ((Language)language_box.getSelectedItem()).getCode();
     653            String value_str = value.getText();
     654            AttributeTableModel model = (AttributeTableModel) element_attributes.getModel();
     655            // Remove the existing one if this is an edit.
     656            if(!add_type && current_attribute != -1) {
     657            model.removeRow(current_attribute);
     658            }
     659            // Add attribute
     660            current_element.addAttribute(name_str, language_code, value_str);
     661            // Update the attribute table
     662            model.add(new Attribute(name_str, language_code, value_str));
     663            model = null;
     664            value_str = null;
     665            language_code = null;
     666            name_str = null;
     667        }
     668        else if(current_set != null) {
     669            String name_str = name.getSelectedItem().toString();
     670            String value_str = value.getText();                     
     671            // First thing we check from profiles is that there isn't already an entry for this name.
     672            AttributeTableModel model = (AttributeTableModel) set_attributes.getModel();
     673            // Remove the existing one if this is an edit.
     674            if(!add_type && current_attribute != -1) {
     675            model.removeRow(current_attribute);
     676            }
     677            // First thing we check from profiles is that there isn't already an entry for this name.
     678            if(!model.contains(name_str, 0)) {
     679            // Add profile
     680            current_set.addAttribute(name_str, value_str);
     681            // Update attribute table.
     682            model.add(new Attribute(name_str, value_str));
     683            }
     684            // Otherwise provide an error message and do not proceed.
     685            else {
     686            cont = false;
     687            JOptionPane.showMessageDialog(self, get("Attribute_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
     688            }
     689            model = null;
     690            value_str = null;
     691            name_str = null;
     692        }
     693        // Hide dialog if we are allowed to continue.
     694        if(cont) {
     695            hide();
     696        }
     697        }
     698        else if(source == cancel_button) {
     699        // Hide dialog
     700        hide();
     701        }
     702        else if(source == name) {
     703        Object object = name.getSelectedItem();
     704        if(object != null) {
     705            java.util.List values = (java.util.List) name_to_values.get(object.toString());
     706            if(values == null) { // Only for collection files, otherwise values != null && values.size() == 0
     707            values = Gatherer.c_man.msm.getElements();
     708            }
     709            value.clear();
     710            for(int i = 0; i < values.size(); i++) {
     711            value.add(values.get(i));
     712            }
     713        }
     714        }
     715        else {
     716        name_to_values.clear();
     717        name.clear();
     718        value.clear();
     719        value.setText("");
     720        // Build the correct name_to_values mapping
     721        // Attributes are slightly tricky as they can come from several sources. Has a collection file been selected...
     722        if(current_collection_file != null) {
     723            target.setText(current_collection_file);
     724            // Name is empty in this one, however values must be the current elements in the collection.                         
     725            language_box.setEnabled(false);
     726        }
     727        // or has an element been selected
     728        else if(current_element != null) {
     729            target.setText(current_element.toString());
     730            // Develop a model for name based on known attributes from all other elements.
     731            java.util.List elements = Gatherer.c_man.msm.getElements();
     732            for(int i = 0; i < elements.size(); i++) {
     733            ElementWrapper element = (ElementWrapper) elements.get(i);
     734            TreeSet attributes = element.getAttributes();
     735            for(Iterator attribute_iterator = attributes.iterator(); attribute_iterator.hasNext(); ) {
     736                Attribute attribute = (Attribute) attribute_iterator.next();
     737                java.util.List values = (java.util.List) name_to_values.get(attribute.name);
     738                if(values == null) {
     739                values = new ArrayList();
     740                name_to_values.put(attribute.name, values);
     741                }
     742                if(!values.contains(attribute.value)) {
     743                values.add(attribute.value);
     744                }
     745                values = null;
     746                attribute = null;
     747            }
     748            attributes = null;
     749            element = null;
     750            }
     751            elements = null;
     752            language_box.setEnabled(true);
     753        }
     754        else if(current_set != null) {
     755            target.setText(current_set.toString());
     756            // Develop a model for name based on known attributes from all other metadata sets. At the same time build a hashmap mapping attribute name to lists of values.
     757            java.util.List sets = Gatherer.c_man.msm.getSets();
     758            for(int i = 0; i < sets.size(); i++) {
     759            MetadataSet set = (MetadataSet) sets.get(i);
     760            NamedNodeMap attributes = set.getAttributes();
     761            for(int j = 0; j < attributes.getLength(); j++) {
     762                Attr attribute = (Attr) attributes.item(j);
     763                String name_str = attribute.getName();
     764                String value_str = attribute.getValue();
     765                java.util.List values = (java.util.List) name_to_values.get(name_str);
     766                if(values == null) {
     767                values = new ArrayList();
     768                name_to_values.put(name_str, values);
     769                }
     770                if(!values.contains(value_str)) {
     771                values.add(value_str);
     772                }
     773                values = null;
     774                value_str = null;
     775                name_str = null;
     776                attribute = null;
     777            }
     778            attributes = null;
     779            set = null;
     780            language_box.setEnabled(false);
     781            }
     782            sets = null;
     783            // If this is an add remove all the attributes already present in the current set.
     784            if(source == add_attribute) {
     785            name.setEnabled(true);
     786            NamedNodeMap attributes = current_set.getAttributes();
     787            for(int i = 0; i < attributes.getLength(); i++) {
     788                Attr attribute = (Attr) attributes.item(i);
     789                String name_str = attribute.getName();
     790                name_to_values.remove(name_str);
     791                name_str = null;
     792                attribute = null;
     793            }
     794            attributes = null;
     795            }
     796        }
     797        // Otherwise we actually disable the name combobox
     798        else {
     799            name.setEnabled(false);
     800        }
     801        // Now name_to_values should contain a list of unique attribute names each mapping to a list of attribute values.
     802        for(Iterator name_iterator = name_to_values.keySet().iterator(); name_iterator.hasNext(); ) {
     803            name.add(name_iterator.next());
     804        }
     805        // Now pritty up the dialog depending on the action type
     806        if(source == add_attribute) {
     807            add_type = true;
     808            setTitle(get("AddAttribute"));
     809            if(current_collection_file != null) {
     810            // Name is empty in this one, however values must be the current elements in the collection.   
     811            java.util.List values = Gatherer.c_man.msm.getElements();
     812            for(int i = 0; i < values.size(); i++) {
     813                value.add(new NameElementWrapperEntry(values.get(i)));
     814            }
     815            values = null;
     816            }
     817            show();
     818        }
     819        else if(current_attribute != -1) {
     820            add_type = false;
     821            setTitle(get("EditAttribute"));
     822            String name_str = (String) profile_attributes.getValueAt(current_attribute, 0);
     823            String value_str = (String) profile_attributes.getValueAt(current_attribute, 1);
     824            // Retrieve the appropriate value model
     825            java.util.List values = (java.util.List) name_to_values.get(name_str);
     826            // Only possible for collection file selections.
     827            if(values == null) {
     828            values = Gatherer.c_man.msm.getElements();
     829            }
     830            name.setSelectedItem(name_str);
     831            name_str = null;
     832            for(int i = 0; i < values.size(); i++) {
     833            value.add(new NameElementWrapperEntry(values.get(i)));
     834            }
     835            values = null;
     836            value.setSelectedItem(value_str);
     837            value_str = null;
     838            show();
     839        }
     840        }
     841        source = null;
     842    }
     843         
     844    public void dispose() {
     845        cancel_button = null;
     846        name = null;
     847        name_to_values = null;
     848        ok_button = null;
     849        target = null;
     850        value = null;
     851    }
     852    }
    853853     
    854     private class AddElementActionListener
    855           extends JDialog
    856           implements ActionListener {
    857           private JButton cancel_button = null;
    858           private JButton ok_button = null;
    859           private JLabel set_field = null;
    860           private JTextField name_field = null;
    861           public AddElementActionListener() {
    862                 super(self);
    863                 setModal(true);
    864                 setSize(ADD_ELEMENT_SIZE);
     854    private class AddElementActionListener
     855    extends JDialog
     856    implements ActionListener {
     857    private JButton cancel_button = null;
     858    private JButton ok_button = null;
     859    private JLabel set_field = null;
     860    private JTextField name_field = null;
     861    public AddElementActionListener() {
     862        super(self);
     863        setModal(true);
     864        setSize(ADD_ELEMENT_SIZE);
    865865                // Creation
    866                 JPanel content_pane = (JPanel) getContentPane();
    867                 content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
    868                 JPanel center_pane = new JPanel();
    869                 center_pane.setOpaque(false);
    870                 JPanel set_pane = new JPanel();
    871                 set_pane.setOpaque(false);
    872                 JLabel set_label = new JLabel(get("Set"));
    873                 set_label.setOpaque(false);
    874                 set_label.setPreferredSize(LABEL_SIZE);
    875                 set_field = new JLabel();
    876                 set_field.setOpaque(false);
    877                 JPanel name_pane = new JPanel();
    878                 name_pane.setOpaque(false);
    879                 JLabel name_label = new JLabel(get("Name"));
    880                 name_label.setOpaque(false);
    881                 name_label.setPreferredSize(LABEL_SIZE);
    882                 name_field = new JTextField();
    883                 name_field.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    884                 name_field.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    885                 name_field.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    886                 name_field.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    887                 JPanel button_pane = new JPanel();
    888                 button_pane.setOpaque(false);
    889                 ok_button = new JButton(get("General.OK"));
    890                 cancel_button = new JButton(get("General.Cancel"));
    891                 TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button);
     866        JPanel content_pane = (JPanel) getContentPane();
     867        content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
     868        JPanel center_pane = new JPanel();
     869        center_pane.setOpaque(false);
     870        JPanel set_pane = new JPanel();
     871        set_pane.setOpaque(false);
     872        JLabel set_label = new JLabel(get("Set"));
     873        set_label.setOpaque(false);
     874        set_label.setPreferredSize(LABEL_SIZE);
     875        set_field = new JLabel();
     876        set_field.setOpaque(false);
     877        JPanel name_pane = new JPanel();
     878        name_pane.setOpaque(false);
     879        JLabel name_label = new JLabel(get("Name"));
     880        name_label.setOpaque(false);
     881        name_label.setPreferredSize(LABEL_SIZE);
     882        name_field = new JTextField();
     883        name_field.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     884        name_field.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     885        name_field.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     886        name_field.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     887        JPanel button_pane = new JPanel();
     888        button_pane.setOpaque(false);
     889        ok_button = new JButton(get("General.OK"));
     890        cancel_button = new JButton(get("General.Cancel"));
     891        TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button);
    892892                // Connection
    893                 cancel_button.addActionListener(this);
    894                 ok_button.addActionListener(this);
    895                 ok_button_enabler.add(name_field);
     893        cancel_button.addActionListener(this);
     894        ok_button.addActionListener(this);
     895        ok_button_enabler.add(name_field);
    896896                // Layout
    897                 set_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    898                 set_pane.setLayout(new BorderLayout());
    899                 set_pane.add(set_label, BorderLayout.WEST);
    900                 set_pane.add(set_field, BorderLayout.CENTER);
    901 
    902                 name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    903                 name_pane.setLayout(new BorderLayout());
    904                 name_pane.add(name_label, BorderLayout.WEST);
    905                 name_pane.add(name_field, BorderLayout.CENTER);
    906 
    907                 center_pane.setLayout(new GridLayout(2,1,0,0));
    908                 center_pane.add(set_pane);
    909                 center_pane.add(name_pane);
    910 
    911                 button_pane.setLayout(new GridLayout(1,2,0,5));
    912                 button_pane.add(ok_button);
    913                 button_pane.add(cancel_button);
    914 
    915                 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    916                 content_pane.setLayout(new BorderLayout());
    917                 content_pane.add(center_pane, BorderLayout.CENTER);
    918                 content_pane.add(button_pane, BorderLayout.SOUTH);
    919 
    920                 setLocation((Gatherer.config.screen_size.width - ADD_ELEMENT_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_ELEMENT_SIZE.height) / 2);
    921           }
    922           /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to
    923             * @param event An <strong>ActionEvent</strong> containing information about the event.
    924             */
    925           public void actionPerformed(ActionEvent event) {
    926                 Object source = event.getSource();
    927                 if(source == ok_button) {
    928                      // Add then dispose
    929                      String name_str = name_field.getText();
    930                      // If this element doesn't already exist.
    931                      if(!current_set.containsElement(name_str)) {
    932                           // Add it,
    933                           ElementWrapper element = current_set.addElement(name_str);
    934                           // Then update the tree
    935                           model.add(current_node, element, MEMNode.ELEMENT);
    936                           // Done
    937                           element = null;
    938                           hide();
    939                      }
    940                      // Otherwise show an error message and do not proceed.
    941                      else {
    942                           JOptionPane.showMessageDialog(self, get("Element_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
    943                      }
    944                      name_str = null;
    945                 }
    946                 else if(source == cancel_button) {
    947                      // Dispose
    948                      hide();
    949                 }
    950                 else {
    951                      if(current_set != null) {
    952                           // You can't manually add elements to the Greenstone metadata set.
    953                           if(!current_set.getNamespace().equals("")) {
    954                                 set_field.setText(current_set.toString());
    955                                 name_field.setText("");
    956                                 // Display
    957                                 show();
    958                           }
    959                           // Warn the user that they can't do that dave.
    960                           else {
    961                                 JOptionPane.showMessageDialog(self, get("Cannot_Add_Elements_To_Greenstone_MDS"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
    962                           }
    963                      }
    964                 }
    965                 source = null;
    966           }
    967 
    968           public void dispose() {
    969                 cancel_button = null;
    970                 ok_button = null;
    971                 name_field = null;
    972                 set_field = null;
    973           }
    974     }
     897        set_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
     898        set_pane.setLayout(new BorderLayout());
     899        set_pane.add(set_label, BorderLayout.WEST);
     900        set_pane.add(set_field, BorderLayout.CENTER);
     901
     902        name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
     903        name_pane.setLayout(new BorderLayout());
     904        name_pane.add(name_label, BorderLayout.WEST);
     905        name_pane.add(name_field, BorderLayout.CENTER);
     906
     907        center_pane.setLayout(new GridLayout(2,1,0,0));
     908        center_pane.add(set_pane);
     909        center_pane.add(name_pane);
     910
     911        button_pane.setLayout(new GridLayout(1,2,0,5));
     912        button_pane.add(ok_button);
     913        button_pane.add(cancel_button);
     914
     915        content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     916        content_pane.setLayout(new BorderLayout());
     917        content_pane.add(center_pane, BorderLayout.CENTER);
     918        content_pane.add(button_pane, BorderLayout.SOUTH);
     919
     920        setLocation((Gatherer.config.screen_size.width - ADD_ELEMENT_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_ELEMENT_SIZE.height) / 2);
     921    }
     922    /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to
     923     * @param event An <strong>ActionEvent</strong> containing information about the event.
     924     */
     925    public void actionPerformed(ActionEvent event) {
     926        Object source = event.getSource();
     927        if(source == ok_button) {
     928        // Add then dispose
     929        String name_str = name_field.getText();
     930        // If this element doesn't already exist.
     931        if(!current_set.containsElement(name_str)) {
     932            // Add it,
     933            ElementWrapper element = current_set.addElement(name_str);
     934            // Then update the tree
     935            model.add(current_node, element, MEMNode.ELEMENT);
     936            // Done
     937            element = null;
     938            hide();
     939        }
     940        // Otherwise show an error message and do not proceed.
     941        else {
     942            JOptionPane.showMessageDialog(self, get("Element_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
     943        }
     944        name_str = null;
     945        }
     946        else if(source == cancel_button) {
     947        // Dispose
     948        hide();
     949        }
     950        else {
     951        if(current_set != null) {
     952            // You can't manually add elements to the Greenstone metadata set.
     953            if(!current_set.getNamespace().equals("")) {
     954            set_field.setText(current_set.toString());
     955            name_field.setText("");
     956            // Display
     957            show();
     958            }
     959            // Warn the user that they can't do that dave.
     960            else {
     961            JOptionPane.showMessageDialog(self, get("Cannot_Add_Elements_To_Greenstone_MDS"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
     962            }
     963        }
     964        }
     965        source = null;
     966    }
     967
     968    public void dispose() {
     969        cancel_button = null;
     970        ok_button = null;
     971        name_field = null;
     972        set_field = null;
     973    }
     974    }
    975975     
    976     private class AddFileActionListener
    977           extends JDialog
    978           implements ActionListener {
    979           private JButton cancel_button = null;
    980           private JButton ok_button = null;
    981           private JTextField name_field = null;
    982           public AddFileActionListener() {
    983                 super(self);
    984                 setModal(true);
    985                 setSize(ADD_FILE_SIZE);
     976    private class AddFileActionListener
     977    extends JDialog
     978    implements ActionListener {
     979    private JButton cancel_button = null;
     980    private JButton ok_button = null;
     981    private JTextField name_field = null;
     982    public AddFileActionListener() {
     983        super(self);
     984        setModal(true);
     985        setSize(ADD_FILE_SIZE);
    986986                // Creation
    987                 JPanel content_pane = (JPanel) getContentPane();
    988                 content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
    989                 JPanel center_pane = new JPanel();
    990                 center_pane.setOpaque(false);
    991                 JPanel profile_pane = new JPanel();
    992                 profile_pane.setOpaque(false);
    993                 JLabel profile_label = new JLabel(get("Profile"));
    994                 profile_label.setOpaque(false);
    995                 profile_label.setPreferredSize(LABEL_SIZE);
    996                 JPanel name_pane = new JPanel();
    997                 name_pane.setOpaque(false);
    998                 JLabel name_label = new JLabel(get("Name"));
    999                 name_label.setOpaque(false);
    1000                 name_label.setPreferredSize(LABEL_SIZE);
    1001                 name_field = new JTextField();
    1002                 name_field.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    1003                 name_field.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    1004                 name_field.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    1005                 name_field.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    1006                 JPanel button_pane = new JPanel();
    1007                 button_pane.setOpaque(false);
    1008                 ok_button = new JButton(get("General.OK"));
    1009                 cancel_button = new JButton(get("General.Cancel"));
    1010                 TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button);
     987        JPanel content_pane = (JPanel) getContentPane();
     988        content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
     989        JPanel center_pane = new JPanel();
     990        center_pane.setOpaque(false);
     991        JPanel profile_pane = new JPanel();
     992        profile_pane.setOpaque(false);
     993        JLabel profile_label = new JLabel(get("Profile"));
     994        profile_label.setOpaque(false);
     995        profile_label.setPreferredSize(LABEL_SIZE);
     996        JPanel name_pane = new JPanel();
     997        name_pane.setOpaque(false);
     998        JLabel name_label = new JLabel(get("Name"));
     999        name_label.setOpaque(false);
     1000        name_label.setPreferredSize(LABEL_SIZE);
     1001        name_field = new JTextField();
     1002        name_field.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     1003        name_field.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     1004        name_field.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     1005        name_field.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     1006        JPanel button_pane = new JPanel();
     1007        button_pane.setOpaque(false);
     1008        ok_button = new JButton(get("General.OK"));
     1009        cancel_button = new JButton(get("General.Cancel"));
     1010        TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button);
    10111011                // Connection
    1012                 cancel_button.addActionListener(this);
    1013                 ok_button.addActionListener(this);
    1014                 ok_button_enabler.add(name_field);
     1012        cancel_button.addActionListener(this);
     1013        ok_button.addActionListener(this);
     1014        ok_button_enabler.add(name_field);
    10151015                // Layout
    1016                 profile_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    1017                 profile_pane.setLayout(new BorderLayout());
    1018                 profile_pane.add(profile_label, BorderLayout.CENTER);
    1019 
    1020                 name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    1021                 name_pane.setLayout(new BorderLayout());
    1022                 name_pane.add(name_label, BorderLayout.WEST);
    1023                 name_pane.add(name_field, BorderLayout.CENTER);
    1024 
    1025                 center_pane.setLayout(new GridLayout(2,1,0,0));
    1026                 center_pane.add(profile_pane);
    1027                 center_pane.add(name_pane);
    1028 
    1029                 button_pane.setLayout(new GridLayout(1,2,0,5));
    1030                 button_pane.add(ok_button);
    1031                 button_pane.add(cancel_button);
    1032 
    1033                 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    1034                 content_pane.setLayout(new BorderLayout());
    1035                 content_pane.add(center_pane, BorderLayout.CENTER);
    1036                 content_pane.add(button_pane, BorderLayout.SOUTH);
    1037 
    1038                 setLocation((Gatherer.config.screen_size.width - ADD_FILE_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_FILE_SIZE.height) / 2);
    1039           }
    1040           /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to
    1041             * @param event An <strong>ActionEvent</strong> containing information about the event.
    1042             */
    1043           public void actionPerformed(ActionEvent event) {
    1044                 Object source = event.getSource();
    1045                 if(source == ok_button) {
    1046                      String name_str = name_field.getText();
    1047                      // Ensure that this source doesn't already exist.
    1048                      if(!Gatherer.c_man.msm.profiler.containsSource(name_str)) {
    1049                           // Add source with empty hashmap of actions.
    1050                           Gatherer.c_man.msm.profiler.addSource(name_str);
    1051                           // Add to tree
    1052                           model.add(model.getProfileNode(), name_str, MEMNode.COLLECTION);
    1053                           hide();
    1054                      }
    1055                      // Otherwise warn the user and don't hide the prompt.
    1056                      else {
    1057                           JOptionPane.showMessageDialog(self, get("File_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
    1058                      }
    1059                      name_str = null;
    1060                 }
    1061                 else if(source == cancel_button) {
    1062                      hide();
    1063                 }
    1064                 else {
    1065                      name_field.setText("");
    1066                      show();
    1067                 }
    1068                 source = null;
    1069           }
    1070 
    1071           public void dispose() {
    1072                 cancel_button = null;
    1073                 ok_button = null;
    1074                 name_field = null;
    1075           }
    1076     }
    1077 
    1078     private class AddSetActionListener
    1079           extends JDialog
    1080           implements ActionListener {
    1081           private JButton cancel_button = null;
    1082           private JButton ok_button = null;
    1083           private JTextField name_field = null;
    1084           private JTextField namespace_field = null;
    1085           public AddSetActionListener() {
    1086                 super(self);
    1087                 setModal(true);
    1088                 setSize(ADD_SET_SIZE);
    1089                 setTitle(get("AddSet"));
     1016        profile_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
     1017        profile_pane.setLayout(new BorderLayout());
     1018        profile_pane.add(profile_label, BorderLayout.CENTER);
     1019
     1020        name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
     1021        name_pane.setLayout(new BorderLayout());
     1022        name_pane.add(name_label, BorderLayout.WEST);
     1023        name_pane.add(name_field, BorderLayout.CENTER);
     1024
     1025        center_pane.setLayout(new GridLayout(2,1,0,0));
     1026        center_pane.add(profile_pane);
     1027        center_pane.add(name_pane);
     1028
     1029        button_pane.setLayout(new GridLayout(1,2,0,5));
     1030        button_pane.add(ok_button);
     1031        button_pane.add(cancel_button);
     1032
     1033        content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     1034        content_pane.setLayout(new BorderLayout());
     1035        content_pane.add(center_pane, BorderLayout.CENTER);
     1036        content_pane.add(button_pane, BorderLayout.SOUTH);
     1037
     1038        setLocation((Gatherer.config.screen_size.width - ADD_FILE_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_FILE_SIZE.height) / 2);
     1039    }
     1040    /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to
     1041     * @param event An <strong>ActionEvent</strong> containing information about the event.
     1042     */
     1043    public void actionPerformed(ActionEvent event) {
     1044        Object source = event.getSource();
     1045        if(source == ok_button) {
     1046        String name_str = name_field.getText();
     1047        // Ensure that this source doesn't already exist.
     1048        if(!Gatherer.c_man.msm.profiler.containsSource(name_str)) {
     1049            // Add source with empty hashmap of actions.
     1050            Gatherer.c_man.msm.profiler.addSource(name_str);
     1051            // Add to tree
     1052            model.add(model.getProfileNode(), name_str, MEMNode.COLLECTION);
     1053            hide();
     1054        }
     1055        // Otherwise warn the user and don't hide the prompt.
     1056        else {
     1057            JOptionPane.showMessageDialog(self, get("File_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
     1058        }
     1059        name_str = null;
     1060        }
     1061        else if(source == cancel_button) {
     1062        hide();
     1063        }
     1064        else {
     1065        name_field.setText("");
     1066        show();
     1067        }
     1068        source = null;
     1069    }
     1070
     1071    public void dispose() {
     1072        cancel_button = null;
     1073        ok_button = null;
     1074        name_field = null;
     1075    }
     1076    }
     1077
     1078    private class AddSetActionListener
     1079    extends JDialog
     1080    implements ActionListener {
     1081    private JButton cancel_button = null;
     1082    private JButton ok_button = null;
     1083    private JTextField name_field = null;
     1084    private JTextField namespace_field = null;
     1085    public AddSetActionListener() {
     1086        super(self);
     1087        setModal(true);
     1088        setSize(ADD_SET_SIZE);
     1089        setTitle(get("AddSet"));
    10901090                // Creation
    1091                 JPanel content_pane = (JPanel) getContentPane();
    1092                 content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
    1093                 JPanel center_pane = new JPanel();
    1094                 center_pane.setOpaque(false);
    1095 
    1096                 JPanel namespace_pane = new JPanel();
    1097                 namespace_pane.setOpaque(false);
    1098                 JLabel namespace_label = new JLabel(get("Namespace"));
    1099                 namespace_label.setOpaque(false);
    1100                 namespace_label.setPreferredSize(LABEL_SIZE);
    1101                 namespace_field = new JTextField();
    1102                 namespace_field.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    1103                 namespace_field.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    1104                 namespace_field.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    1105                 namespace_field.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    1106 
    1107                 JPanel name_pane = new JPanel();
    1108                 name_pane.setOpaque(false);
    1109                 JLabel name_label = new JLabel(get("Name"));
    1110                 name_label.setOpaque(false);
    1111                 name_label.setPreferredSize(LABEL_SIZE);
    1112                 name_field = new JTextField();
    1113                 name_field.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    1114                 name_field.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    1115                 name_field.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    1116                 name_field.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    1117 
    1118                 JPanel button_pane = new JPanel();
    1119                 button_pane.setOpaque(false);
    1120                 ok_button = new JButton(get("General.OK"));
    1121                 ok_button.setEnabled(false);
    1122                 cancel_button = new JButton(get("General.Cancel"));
    1123                 TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button);
     1091        JPanel content_pane = (JPanel) getContentPane();
     1092        content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
     1093        JPanel center_pane = new JPanel();
     1094        center_pane.setOpaque(false);
     1095
     1096        JPanel namespace_pane = new JPanel();
     1097        namespace_pane.setOpaque(false);
     1098        JLabel namespace_label = new JLabel(get("Namespace"));
     1099        namespace_label.setOpaque(false);
     1100        namespace_label.setPreferredSize(LABEL_SIZE);
     1101        namespace_field = new JTextField();
     1102        namespace_field.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     1103        namespace_field.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     1104        namespace_field.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     1105        namespace_field.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     1106
     1107        JPanel name_pane = new JPanel();
     1108        name_pane.setOpaque(false);
     1109        JLabel name_label = new JLabel(get("Name"));
     1110        name_label.setOpaque(false);
     1111        name_label.setPreferredSize(LABEL_SIZE);
     1112        name_field = new JTextField();
     1113        name_field.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     1114        name_field.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     1115        name_field.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     1116        name_field.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     1117
     1118        JPanel button_pane = new JPanel();
     1119        button_pane.setOpaque(false);
     1120        ok_button = new JButton(get("General.OK"));
     1121        ok_button.setEnabled(false);
     1122        cancel_button = new JButton(get("General.Cancel"));
     1123        TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button);
    11241124                // Connection
    1125                 cancel_button.addActionListener(this);
    1126                 ok_button.addActionListener(this);
    1127                 ok_button_enabler.add(name_field);
    1128                 ok_button_enabler.add(namespace_field);
     1125        cancel_button.addActionListener(this);
     1126        ok_button.addActionListener(this);
     1127        ok_button_enabler.add(name_field);
     1128        ok_button_enabler.add(namespace_field);
    11291129                // Layout
    1130                 namespace_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    1131                 namespace_pane.setLayout(new BorderLayout());
    1132                 namespace_pane.add(namespace_label, BorderLayout.WEST);
    1133                 namespace_pane.add(namespace_field, BorderLayout.CENTER);
    1134 
    1135                 name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    1136                 name_pane.setLayout(new BorderLayout());
    1137                 name_pane.add(name_label, BorderLayout.WEST);
    1138                 name_pane.add(name_field, BorderLayout.CENTER);
    1139 
    1140                 center_pane.setLayout(new GridLayout(2,1));
    1141                 center_pane.add(namespace_pane);
    1142                 center_pane.add(name_pane);
    1143 
    1144                 button_pane.setLayout(new GridLayout(1,2,0,5));
    1145                 button_pane.add(ok_button);
    1146                 button_pane.add(cancel_button);
    1147 
    1148                 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    1149                 content_pane.setLayout(new BorderLayout());
    1150                 content_pane.add(center_pane, BorderLayout.CENTER);
    1151                 content_pane.add(button_pane, BorderLayout.SOUTH);
    1152 
    1153                 setLocation((Gatherer.config.screen_size.width - ADD_SET_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_SET_SIZE.height) / 2);
    1154           }
    1155           /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to
    1156             * @param event An <strong>ActionEvent</strong> containing information about the event.
    1157             */
    1158           public void actionPerformed(ActionEvent event) {
    1159                 Object source = event.getSource();
    1160                 if(source == ok_button) {
    1161                      String namespace_str = namespace_field.getText();
    1162                      String name_str = name_field.getText();
    1163                      // Ensure the set doesn't already exist
    1164                      if(Gatherer.c_man.msm.getSet(name_str) == null) {
    1165                           MetadataSet set = Gatherer.c_man.msm.addSet(namespace_str, name_str);
    1166                           // Update tree.
    1167                           model.add(null, set, MEMNode.SET);
    1168                           // Done
    1169                           set = null;
    1170                           hide();
    1171                      }
    1172                      // Otherwise show a warning.
    1173                      else {
    1174                           JOptionPane.showMessageDialog(self, get("Set_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
    1175                      }
    1176                      name_str = null;
    1177                      namespace_str = null;
    1178                 }
    1179                 else if(source == cancel_button) {
    1180                      hide();
    1181                 }
    1182                 else {
    1183                      name_field.setText("");
    1184                      show();
    1185                 }
    1186           }
    1187          
    1188           public void dispose() {
    1189                 cancel_button = null;
    1190                 ok_button = null;
    1191                 name_field = null;
    1192           }
    1193     }
     1130        namespace_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
     1131        namespace_pane.setLayout(new BorderLayout());
     1132        namespace_pane.add(namespace_label, BorderLayout.WEST);
     1133        namespace_pane.add(namespace_field, BorderLayout.CENTER);
     1134
     1135        name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
     1136        name_pane.setLayout(new BorderLayout());
     1137        name_pane.add(name_label, BorderLayout.WEST);
     1138        name_pane.add(name_field, BorderLayout.CENTER);
     1139
     1140        center_pane.setLayout(new GridLayout(2,1));
     1141        center_pane.add(namespace_pane);
     1142        center_pane.add(name_pane);
     1143
     1144        button_pane.setLayout(new GridLayout(1,2,0,5));
     1145        button_pane.add(ok_button);
     1146        button_pane.add(cancel_button);
     1147
     1148        content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     1149        content_pane.setLayout(new BorderLayout());
     1150        content_pane.add(center_pane, BorderLayout.CENTER);
     1151        content_pane.add(button_pane, BorderLayout.SOUTH);
     1152
     1153        setLocation((Gatherer.config.screen_size.width - ADD_SET_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_SET_SIZE.height) / 2);
     1154    }
     1155    /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to
     1156     * @param event An <strong>ActionEvent</strong> containing information about the event.
     1157     */
     1158    public void actionPerformed(ActionEvent event) {
     1159        Object source = event.getSource();
     1160        if(source == ok_button) {
     1161        String namespace_str = namespace_field.getText();
     1162        String name_str = name_field.getText();
     1163        // Ensure the set doesn't already exist
     1164        if(Gatherer.c_man.msm.getSet(name_str) == null) {
     1165            MetadataSet set = Gatherer.c_man.msm.addSet(namespace_str, name_str);
     1166            // Update tree.
     1167            model.add(null, set, MEMNode.SET);
     1168            // Done
     1169            set = null;
     1170            hide();
     1171        }
     1172        // Otherwise show a warning.
     1173        else {
     1174            JOptionPane.showMessageDialog(self, get("Set_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
     1175        }
     1176        name_str = null;
     1177        namespace_str = null;
     1178        }
     1179        else if(source == cancel_button) {
     1180        hide();
     1181        }
     1182        else {
     1183        name_field.setText("");
     1184        show();
     1185        }
     1186    }
     1187         
     1188    public void dispose() {
     1189        cancel_button = null;
     1190        ok_button = null;
     1191        name_field = null;
     1192    }
     1193    }
    11941194     
    1195     private class AddOrEditValueActionListener
    1196           extends JDialog
    1197           implements ActionListener, TreeSelectionListener {
    1198           private boolean add_type = true;
    1199           private boolean ignore = false;
    1200           private GValueNode subject_node = null;
    1201           private JButton cancel_button = null;
    1202           private JButton ok_button = null;
    1203           private JTextArea value = null;
    1204           private JTextField alias = null;
    1205           private SmarterTree subject_tree = null;
    1206 
    1207           /** Constructor. */
    1208           public AddOrEditValueActionListener() {
    1209                 super(self);
    1210                 this.setModal(true);
    1211                 this.setSize(ADD_OR_EDIT_VALUE_SIZE);
     1195    private class AddOrEditValueActionListener
     1196    extends JDialog
     1197    implements ActionListener, TreeSelectionListener {
     1198    private boolean add_type = true;
     1199    private boolean ignore = false;
     1200    private GValueNode subject_node = null;
     1201    private JButton cancel_button = null;
     1202    private JButton ok_button = null;
     1203    private JTextArea value = null;
     1204    private JTextField alias = null;
     1205    private SmarterTree subject_tree = null;
     1206
     1207    /** Constructor. */
     1208    public AddOrEditValueActionListener() {
     1209        super(self);
     1210        this.setModal(true);
     1211        this.setSize(ADD_OR_EDIT_VALUE_SIZE);
    12121212                // Create
    1213                 JPanel content_pane = (JPanel) getContentPane();
    1214                 content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
    1215                 JPanel center_pane = new JPanel();
    1216                 center_pane.setOpaque(false);
    1217                 JPanel inner_pane = new JPanel();
    1218                 inner_pane.setOpaque(false);
    1219                 JPanel subject_tree_pane = new JPanel();
    1220                 subject_tree_pane.setOpaque(false);
    1221                 JLabel subject_tree_label = new JLabel(get("Subject"));
    1222                 subject_tree_label.setOpaque(false);
    1223                 subject_tree = new SmarterTree();
    1224                 subject_tree.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    1225                 subject_tree.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    1226                 subject_tree.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    1227                 subject_tree.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    1228                 JPanel value_pane = new JPanel();
    1229                 value_pane.setOpaque(false);
    1230                 JLabel value_label = new JLabel(get("Value"));
    1231                 value_label.setOpaque(false);
    1232                 value = new JTextArea();
    1233                 value.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    1234                 value.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    1235                 value.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    1236                 value.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    1237                 JPanel alias_pane = new JPanel();
    1238                 alias_pane.setOpaque(false);
    1239                 JLabel alias_label = new JLabel(get("Alias"));
    1240                 alias_label.setOpaque(false);
    1241                 alias_label.setPreferredSize(LABEL_SIZE);
    1242                 alias = new JTextField();
    1243                 alias.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    1244                 alias.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
    1245                 alias.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
    1246                 alias.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
    1247                 JPanel button_pane = new JPanel();
    1248                 button_pane.setOpaque(false);
    1249                 ok_button = new JButton(get("General.OK"));
    1250                 ok_button.setEnabled(false);
    1251                 cancel_button = new JButton(get("General.Cancel"));
     1213        JPanel content_pane = (JPanel) getContentPane();
     1214        content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
     1215        JPanel center_pane = new JPanel();
     1216        center_pane.setOpaque(false);
     1217        JPanel inner_pane = new JPanel();
     1218        inner_pane.setOpaque(false);
     1219        JPanel subject_tree_pane = new JPanel();
     1220        subject_tree_pane.setOpaque(false);
     1221        JLabel subject_tree_label = new JLabel(get("Subject"));
     1222        subject_tree_label.setOpaque(false);
     1223        subject_tree = new SmarterTree();
     1224        subject_tree.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     1225        subject_tree.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     1226        subject_tree.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     1227        subject_tree.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     1228        JPanel value_pane = new JPanel();
     1229        value_pane.setOpaque(false);
     1230        JLabel value_label = new JLabel(get("Value"));
     1231        value_label.setOpaque(false);
     1232        value = new JTextArea();
     1233        value.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     1234        value.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     1235        value.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     1236        value.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     1237        JPanel alias_pane = new JPanel();
     1238        alias_pane.setOpaque(false);
     1239        JLabel alias_label = new JLabel(get("Alias"));
     1240        alias_label.setOpaque(false);
     1241        alias_label.setPreferredSize(LABEL_SIZE);
     1242        alias = new JTextField();
     1243        alias.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     1244        alias.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
     1245        alias.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
     1246        alias.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
     1247        JPanel button_pane = new JPanel();
     1248        button_pane.setOpaque(false);
     1249        ok_button = new JButton(get("General.OK"));
     1250        ok_button.setEnabled(false);
     1251        cancel_button = new JButton(get("General.Cancel"));
    12521252                // Connect
    1253                 TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button);
    1254                 cancel_button.addActionListener(this);
    1255                 ok_button.addActionListener(this);
    1256                 subject_tree.addTreeSelectionListener(this);
    1257                 ok_button_enabler.add(value);
     1253        TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button);
     1254        cancel_button.addActionListener(this);
     1255        ok_button.addActionListener(this);
     1256        subject_tree.addTreeSelectionListener(this);
     1257        ok_button_enabler.add(value);
    12581258                // Layout
    1259                 subject_tree_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    1260                 subject_tree_pane.setLayout(new BorderLayout());
    1261                 subject_tree_pane.add(subject_tree_label, BorderLayout.NORTH);
    1262                 subject_tree_pane.add(new JScrollPane(subject_tree), BorderLayout.CENTER);
    1263 
    1264                 value_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
    1265                 value_pane.setLayout(new BorderLayout());
    1266                 value_pane.add(value_label, BorderLayout.NORTH);
    1267                 value_pane.add(new JScrollPane(value), BorderLayout.CENTER);
     1259        subject_tree_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     1260        subject_tree_pane.setLayout(new BorderLayout());
     1261        subject_tree_pane.add(subject_tree_label, BorderLayout.NORTH);
     1262        subject_tree_pane.add(new JScrollPane(subject_tree), BorderLayout.CENTER);
     1263
     1264        value_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
     1265        value_pane.setLayout(new BorderLayout());
     1266        value_pane.add(value_label, BorderLayout.NORTH);
     1267        value_pane.add(new JScrollPane(value), BorderLayout.CENTER);
    12681268               
    1269                 inner_pane.setLayout(new GridLayout(2,1));
    1270                 inner_pane.add(subject_tree_pane);
    1271                 inner_pane.add(value_pane);
    1272 
    1273                 alias_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
    1274                 alias_pane.setLayout(new BorderLayout());
    1275                 alias_pane.add(alias_label, BorderLayout.WEST);
    1276                 alias_pane.add(alias, BorderLayout.CENTER);
    1277 
    1278                 center_pane.setLayout(new BorderLayout());
    1279                 center_pane.add(inner_pane, BorderLayout.CENTER);
    1280                 center_pane.add(alias_pane, BorderLayout.SOUTH);
    1281 
    1282                 button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
    1283                 button_pane.setLayout(new GridLayout(1,2,5,5));
    1284                 button_pane.add(ok_button);
    1285                 button_pane.add(cancel_button);
    1286 
    1287                 content_pane.setLayout(new BorderLayout());
    1288                 content_pane.add(center_pane, BorderLayout.CENTER);
    1289                 content_pane.add(button_pane, BorderLayout.SOUTH);
    1290                 setLocation((Gatherer.config.screen_size.width - ADD_OR_EDIT_VALUE_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_OR_EDIT_VALUE_SIZE.height) / 2);
    1291           }
    1292           /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to add a new metadata value to the assigned values tree.
    1293             * @param event An <strong>ActionEvent</strong> containing information about the event.
    1294             */
    1295           public void actionPerformed(ActionEvent event) {
    1296                 Object source = event.getSource();
    1297                 if(source == ok_button) {
    1298                      // Now we action as necessary
    1299                      GValueModel model = Gatherer.c_man.msm.getValueTree(current_element);
    1300                      if(add_type) {
    1301                           // Add this value to the tree.
    1302                           GValueNode parent = null;
    1303                           model.addValue(value.getText(), subject_node, alias.getText());
    1304                           parent = null;
    1305                      }
    1306                      else {
    1307                           // Rewrite this value in the DOM model. Note that this will automatically rewrite all assignments as they are live references.
    1308                           current_value_node.setValue(value.getText());
    1309                           current_value_node.setAlias(alias.getText());
    1310                           // Now insert the node into the currently selected subject, but only if the parent has changed.
    1311                           if(subject_node != current_value_node.getParent()) {
    1312                                 GValueNode old_subject_node = (GValueNode) current_value_node.getParent();
    1313                                 // Find the new values position in subject_nodes children.
    1314                                 GValueNode sibling = null;
    1315                                 int index = 0;
    1316                                 boolean found = false;
    1317                                 while(index < subject_node.getChildCount() && !found) {
    1318                                     sibling = (GValueNode) subject_node.getChildAt(index);
    1319                                     int order = current_value_node.toString().compareToIgnoreCase(sibling.toString());
    1320                                     // If the sibling is 'greater than' or comes after current value then insert.
    1321                                     if(order < 0) {
    1322                                           // Insert. This will coincidently remove from original parent.
    1323                                           Node parent_node = subject_node.getElement();
    1324                                           Node child_node = current_value_node.getElement();
    1325                                           Node sibling_node = sibling.getElement();
    1326                                           parent_node.insertBefore(child_node, sibling_node);
    1327                                           found = true;
    1328                                     }
    1329                                     // The value already exists exactly as is. In theory this case can never happenm but just incase do nothing more.
    1330                                     else if(order == 0) {
    1331                                           found = true;
    1332                                     }
    1333                                     // The sibling is 'less than' or before the current value, keep looking.
    1334                                     else {
    1335                                           index++;
    1336                                     }
    1337                                 }
    1338                                 // If we haven't done so yet, insert the current node. This will coincidently remove from original parent.
    1339                                 if(!found) {
    1340                                     Node parent_node = subject_node.getElement();
    1341                                     Node child_node = current_value_node.getElement();
    1342                                     parent_node.appendChild(child_node);
    1343                                 }
    1344                                 // Inform the tree model what two nodes structures have changed (origin and destination).
    1345                                 subject_node.unmap();
    1346                                 old_subject_node.unmap();
    1347                                 model.nodeStructureChanged(old_subject_node);
    1348                                 model.nodeStructureChanged(subject_node);
    1349                           }
    1350                           // And if a data change was made tell the tree its data model is ka-bluey.
    1351                           else {
    1352                                 model.nodeChanged(current_value_node);
    1353                           }
    1354                      }
    1355                      model = null;
    1356                      // Hide dialog
    1357                      hide();
    1358                 }
    1359                 else if(source == cancel_button) {
    1360                      // Hide dialog
    1361                      hide();
    1362                 }
    1363                 else {
    1364                      // Reset dialog
    1365                      // current_value_node
    1366                      GValueModel model = Gatherer.c_man.msm.getValueTree(current_element);
    1367                      subject_tree.setModel(model);
    1368                      // Task specific
    1369                      if(source == add_value) {
    1370                           add_type = true;
    1371                           if(current_value_node != null) {
    1372                                 subject_node = current_value_node;
    1373                           }
    1374                           else {
    1375                                 subject_node = (GValueNode) model.getRoot();
    1376                           }
    1377                           value.setText("");
    1378                           alias.setText("");
    1379                           setTitle(get("AddValue"));
    1380                      }
    1381                      else {
    1382                           add_type = false;
    1383                           if(current_value_node != null) {
    1384                                 subject_node = (GValueNode) current_value_node.getParent();
    1385                           }
    1386                           else {
    1387                                 subject_node = (GValueNode) model.getRoot();
    1388                           }
    1389                           value.setText(current_value_node.toString());
    1390                           alias.setText(current_value_node.getAlias());
    1391                           setTitle(get("EditValue"));
    1392                      }
    1393                      model = null;
    1394                      if(subject_node != null) {
    1395                           TreePath path = new TreePath(subject_node.getPath());
    1396                           subject_tree.scrollPathToVisible(path);
    1397                           subject_tree.setSelectionPath(path);
    1398                           path = null;
    1399                      }
    1400                      // Display
    1401                      show();
    1402                 }
    1403           }
    1404 
    1405           public void dispose() {
    1406                 alias = null;
    1407                 cancel_button = null;
    1408                 ok_button = null;
    1409                 subject_node = null;
    1410                 subject_tree = null;
    1411                 value = null;
    1412           }
    1413 
    1414           public void valueChanged(TreeSelectionEvent event) {
    1415                 if(subject_tree.getSelectionCount() > 0 && !ignore) {
    1416                      ignore = true;
    1417                      TreePath selected_path = subject_tree.getSelectionPath();
    1418                      GValueNode requested_node = (GValueNode) selected_path.getLastPathComponent();
    1419                      // Ensure the requested node is not a descendant of the current_value_node
    1420                      if(current_value_node != null) {
    1421                           if(!add_type && current_value_node.isNodeDescendant(requested_node)) {
    1422                                 TreePath path = new TreePath(subject_node.getPath());
    1423                                 subject_tree.scrollPathToVisible(path);
    1424                                 subject_tree.setSelectionPath(path);
    1425                           }
    1426                           else {
    1427                                 subject_node = requested_node;
    1428                           }
    1429                      }
    1430                      selected_path = null;
    1431                      ignore = false;
    1432                 }
    1433           }
    1434     }
     1269        inner_pane.setLayout(new GridLayout(2,1));
     1270        inner_pane.add(subject_tree_pane);
     1271        inner_pane.add(value_pane);
     1272
     1273        alias_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
     1274        alias_pane.setLayout(new BorderLayout());
     1275        alias_pane.add(alias_label, BorderLayout.WEST);
     1276        alias_pane.add(alias, BorderLayout.CENTER);
     1277
     1278        center_pane.setLayout(new BorderLayout());
     1279        center_pane.add(inner_pane, BorderLayout.CENTER);
     1280        center_pane.add(alias_pane, BorderLayout.SOUTH);
     1281
     1282        button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
     1283        button_pane.setLayout(new GridLayout(1,2,5,5));
     1284        button_pane.add(ok_button);
     1285        button_pane.add(cancel_button);
     1286
     1287        content_pane.setLayout(new BorderLayout());
     1288        content_pane.add(center_pane, BorderLayout.CENTER);
     1289        content_pane.add(button_pane, BorderLayout.SOUTH);
     1290        setLocation((Gatherer.config.screen_size.width - ADD_OR_EDIT_VALUE_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_OR_EDIT_VALUE_SIZE.height) / 2);
     1291    }
     1292    /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to add a new metadata value to the assigned values tree.
     1293     * @param event An <strong>ActionEvent</strong> containing information about the event.
     1294     */
     1295    public void actionPerformed(ActionEvent event) {
     1296        Object source = event.getSource();
     1297        if(source == ok_button) {
     1298        // Now we action as necessary
     1299        GValueModel model = Gatherer.c_man.msm.getValueTree(current_element);
     1300        if(add_type) {
     1301            // Add this value to the tree.
     1302            GValueNode parent = null;
     1303            model.addValue(value.getText(), subject_node, alias.getText());
     1304            parent = null;
     1305        }
     1306        else {
     1307            // Rewrite this value in the DOM model. Note that this will automatically rewrite all assignments as they are live references.
     1308            current_value_node.setValue(value.getText());
     1309            current_value_node.setAlias(alias.getText());
     1310            // Now insert the node into the currently selected subject, but only if the parent has changed.
     1311            if(subject_node != current_value_node.getParent()) {
     1312            GValueNode old_subject_node = (GValueNode) current_value_node.getParent();
     1313            // Find the new values position in subject_nodes children.
     1314            GValueNode sibling = null;
     1315            int index = 0;
     1316            boolean found = false;
     1317            while(index < subject_node.getChildCount() && !found) {
     1318                sibling = (GValueNode) subject_node.getChildAt(index);
     1319                int order = current_value_node.toString().compareToIgnoreCase(sibling.toString());
     1320                // If the sibling is 'greater than' or comes after current value then insert.
     1321                if(order < 0) {
     1322                // Insert. This will coincidently remove from original parent.
     1323                Node parent_node = subject_node.getElement();
     1324                Node child_node = current_value_node.getElement();
     1325                Node sibling_node = sibling.getElement();
     1326                parent_node.insertBefore(child_node, sibling_node);
     1327                found = true;
     1328                }
     1329                // The value already exists exactly as is. In theory this case can never happenm but just incase do nothing more.
     1330                else if(order == 0) {
     1331                found = true;
     1332                }
     1333                // The sibling is 'less than' or before the current value, keep looking.
     1334                else {
     1335                index++;
     1336                }
     1337            }
     1338            // If we haven't done so yet, insert the current node. This will coincidently remove from original parent.
     1339            if(!found) {
     1340                Node parent_node = subject_node.getElement();
     1341                Node child_node = current_value_node.getElement();
     1342                parent_node.appendChild(child_node);
     1343            }
     1344            // Inform the tree model what two nodes structures have changed (origin and destination).
     1345            subject_node.unmap();
     1346            old_subject_node.unmap();
     1347            model.nodeStructureChanged(old_subject_node);
     1348            model.nodeStructureChanged(subject_node);
     1349            }
     1350            // And if a data change was made tell the tree its data model is ka-bluey.
     1351            else {
     1352            model.nodeChanged(current_value_node);
     1353            }
     1354        }
     1355        model = null;
     1356        // Hide dialog
     1357        hide();
     1358        }
     1359        else if(source == cancel_button) {
     1360        // Hide dialog
     1361        hide();
     1362        }
     1363        else {
     1364        // Reset dialog
     1365        // current_value_node
     1366        GValueModel model = Gatherer.c_man.msm.getValueTree(current_element);
     1367        subject_tree.setModel(model);
     1368        // Task specific
     1369        if(source == add_value) {
     1370            add_type = true;
     1371            if(current_value_node != null) {
     1372            subject_node = current_value_node;
     1373            }
     1374            else {
     1375            subject_node = (GValueNode) model.getRoot();
     1376            }
     1377            value.setText("");
     1378            alias.setText("");
     1379            setTitle(get("AddValue"));
     1380        }
     1381        else {
     1382            add_type = false;
     1383            if(current_value_node != null) {
     1384            subject_node = (GValueNode) current_value_node.getParent();
     1385            }
     1386            else {
     1387            subject_node = (GValueNode) model.getRoot();
     1388            }
     1389            value.setText(current_value_node.toString());
     1390            alias.setText(current_value_node.getAlias());
     1391            setTitle(get("EditValue"));
     1392        }
     1393        model = null;
     1394        if(subject_node != null) {
     1395            TreePath path = new TreePath(subject_node.getPath());
     1396            subject_tree.scrollPathToVisible(path);
     1397            subject_tree.setSelectionPath(path);
     1398            path = null;
     1399        }
     1400        // Display
     1401        show();
     1402        }
     1403    }
     1404
     1405    public void dispose() {
     1406        alias = null;
     1407        cancel_button = null;
     1408        ok_button = null;
     1409        subject_node = null;
     1410        subject_tree = null;
     1411        value = null;
     1412    }
     1413
     1414    public void valueChanged(TreeSelectionEvent event) {
     1415        if(subject_tree.getSelectionCount() > 0 && !ignore) {
     1416        ignore = true;
     1417        TreePath selected_path = subject_tree.getSelectionPath();
     1418        GValueNode requested_node = (GValueNode) selected_path.getLastPathComponent();
     1419        // Ensure the requested node is not a descendant of the current_value_node
     1420        if(current_value_node != null) {
     1421            if(!add_type && current_value_node.isNodeDescendant(requested_node)) {
     1422            TreePath path = new TreePath(subject_node.getPath());
     1423            subject_tree.scrollPathToVisible(path);
     1424            subject_tree.setSelectionPath(path);
     1425            }
     1426            else {
     1427            subject_node = requested_node;
     1428            }
     1429        }
     1430        selected_path = null;
     1431        ignore = false;
     1432        }
     1433    }
     1434    }
    14351435     
    1436     private class CloseActionListener
    1437         extends JDialog
    1438         implements ActionListener {
    1439         /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to close the editor dialog.
    1440         * @param event An <strong>ActionEvent</strong> containing information about the event.
    1441         */
    1442         public void actionPerformed(ActionEvent event) {
    1443             self.dispose();
    1444         }
    1445     }
    1446 
    1447     private class RemoveAttributeActionListener
    1448           implements ActionListener {
    1449           /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to
    1450             * @param event An <strong>ActionEvent</strong> containing information about the event.
    1451             */
    1452           public void actionPerformed(ActionEvent event) {
    1453                 if(current_attribute != -1) {
    1454                      int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("Attribute")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]);
    1455                      // Remove this attribute
    1456                      if(result == 0) {
    1457                           ignore = true;
    1458                           // Attributes are tricky little beasties, as they can come from several different places and depend upon what other little beasties have been selected
    1459                           // Has a collection file been selected...
    1460                           if(current_collection_file != null) {
    1461                                 // Remove a profile
    1462                                 String source = (String) profile_attributes.getValueAt(current_attribute, 0);
    1463                                 Gatherer.c_man.msm.profiler.removeAction(current_collection_file, source);
    1464                                 source = null;
    1465                                 // Refresh table
    1466                                 ((AttributeTableModel)profile_attributes.getModel()).removeRow(current_attribute);
    1467                           }
    1468                           // or has an element been selected
    1469                           else if(current_element != null) {
    1470                                 // Remove element attribute
    1471                                 String name = (String) element_attributes.getValueAt(current_attribute, 0);
    1472                                 String value = (String) element_attributes.getValueAt(current_attribute, 1);
    1473                                 current_element.removeAttribute(name, value); // Can be multiple authors for instance
    1474                                 // Refresh table
    1475                                 ((AttributeTableModel)element_attributes.getModel()).removeRow(current_attribute);
    1476                           }
    1477                           else if(current_set != null) {
    1478                                 String name = (String) set_attributes.getValueAt(current_attribute, 0);
    1479                                 // Remove set attribute
    1480                                 current_set.removeAttribute(name);
    1481                                 // Refresh table
    1482                                 ((AttributeTableModel)set_attributes.getModel()).removeRow(current_attribute);
    1483                           }
    1484                           // Disable buttons
    1485                           edit_attribute.setEnabled(false);
    1486                           remove_attribute.setEnabled(false);
    1487                           ignore = false;
    1488                      }
    1489                 }
    1490           }
    1491     }
    1492 
    1493     private class RemoveElementActionListener
    1494           implements ActionListener {
    1495           /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to provide an editing prompt.
    1496             * @param event An <strong>ActionEvent</strong> containing information about the event.
    1497             */
    1498           public void actionPerformed(ActionEvent event) {
    1499                 if(current_element != null) {
    1500                      int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("Element")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]);
    1501                      // Remove this attribute
    1502                      if(result == 0) {
    1503                           ignore = true;
    1504                           Gatherer.c_man.msm.removeElement(current_element);
    1505                           // Clear selection
    1506                           mds_tree.clearSelection();
    1507                           model.remove(current_element.toString(), MEMNode.ELEMENT);
    1508                           // Meanwhile disable/enable controls given we had an element selected, but no longer do.
    1509                           remove_element.setEnabled(false);
    1510                           // Show a blank panel.
    1511                           card_layout.show(details_pane, BLANK);
    1512                           ignore = false;
    1513                      }
    1514                 }
    1515                 else {
    1516                      ///ystem.err.println("No current element selected.");
    1517                 }
    1518           }
    1519     }
    1520 
    1521     private class RemoveFileActionListener
    1522           implements ActionListener {
    1523           /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to provide an editing prompt.
    1524             * @param event An <strong>ActionEvent</strong> containing information about the event.
    1525             */
    1526           public void actionPerformed(ActionEvent event) {
    1527                 if(current_collection_file != null) {
    1528                      int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("File")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]);
    1529                      // Remove the current collection file profile from the profiler.
    1530                      if(result == 0) {
    1531                           ignore = true;
    1532                           Gatherer.c_man.msm.profiler.removeProfile(current_collection_file);
    1533 // Clear selection
    1534                           mds_tree.clearSelection();
    1535                           model.remove(current_collection_file, MEMNode.COLLECTION);
    1536                           // Meanwhile disable/enable controls given we had a set selected, but no longer do.
    1537                           remove_file.setEnabled(false);
    1538                           // Show a blank panel.
    1539                           card_layout.show(details_pane, BLANK);
    1540                           ignore = false;
    1541                      }
    1542                 }
    1543           }
    1544     }
    1545 
    1546     private class RemoveSetActionListener
    1547           implements ActionListener {
    1548           /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to
    1549             * @param event An <strong>ActionEvent</strong> containing information about the event.
    1550             */
    1551           public void actionPerformed(ActionEvent event) {
    1552                 if(current_set != null) {
    1553                      int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("Set")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]);
    1554                      // Remove the currently selected set
    1555                      if(result == 0) {
    1556                           ignore = true;
    1557                           Gatherer.c_man.msm.removeSet(current_set);
    1558                           // Clear selection
    1559                           mds_tree.clearSelection();
    1560                           model.remove(current_set.toString(), MEMNode.SET);
    1561                           // Meanwhile disable/enable controls given we had a set selected, but no longer do.
    1562                           remove_set.setEnabled(false);
    1563                           // Show a blank panel.
    1564                           card_layout.show(details_pane, BLANK);
    1565                           ignore = false;
    1566                      }
    1567                 }
    1568           }
    1569     }
    1570     /** This class will remove the currently selected metadata or profile value when any registered control is actioned. Note that removing a value acts differently from the other removes in that if you remove a value which is still assigned somewhere the values are restored next time said assignment is viewed. This turned out far easier and reasonable to code than attempting to remove all remaining values when you delete its reference in the GValueModel. */
    1571     private class RemoveValueActionListener
    1572           implements ActionListener {
    1573           /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to
    1574             * @param event An <strong>ActionEvent</strong> containing information about the event.
    1575             */
    1576           public void actionPerformed(ActionEvent event) {
    1577                 if(current_value_node != null) {                 
    1578                      int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("Value")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]);
    1579                      // Remove the current selected value
    1580                      if(result == 0) {
    1581                           ignore = true;
    1582                           GValueModel model = (GValueModel) element_values.getModel();
    1583                           model.removeValue(current_value_node);
    1584                           // Meanwhile disable/enable controls given we had a value selected, but no longer do.
    1585                           edit_value.setEnabled(false);
    1586                           remove_value.setEnabled(false);
    1587                           ignore = false;
    1588                      }
    1589                 }
    1590           }
    1591     }
     1436    private class CloseActionListener
     1437    extends JDialog
     1438    implements ActionListener {
     1439    /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to close the editor dialog.
     1440    * @param event An <strong>ActionEvent</strong> containing information about the event.
     1441    */
     1442    public void actionPerformed(ActionEvent event) {
     1443        self.dispose();
     1444    }
     1445    }
     1446
     1447    private class RemoveAttributeActionListener
     1448    implements ActionListener {
     1449    /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to
     1450     * @param event An <strong>ActionEvent</strong> containing information about the event.
     1451     */
     1452    public void actionPerformed(ActionEvent event) {
     1453        if(current_attribute != -1) {
     1454        int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("Attribute")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]);
     1455        // Remove this attribute
     1456        if(result == 0) {
     1457            ignore = true;
     1458            // Attributes are tricky little beasties, as they can come from several different places and depend upon what other little beasties have been selected
     1459            // Has a collection file been selected...
     1460            if(current_collection_file != null) {
     1461            // Remove a profile
     1462            String source = (String) profile_attributes.getValueAt(current_attribute, 0);
     1463            Gatherer.c_man.msm.profiler.removeAction(current_collection_file, source);
     1464            source = null;
     1465            // Refresh table
     1466            ((AttributeTableModel)profile_attributes.getModel()).removeRow(current_attribute);
     1467            }
     1468            // or has an element been selected
     1469            else if(current_element != null) {
     1470            // Remove element attribute
     1471            String name = (String) element_attributes.getValueAt(current_attribute, 0);
     1472            String value = (String) element_attributes.getValueAt(current_attribute, 1);
     1473            current_element.removeAttribute(name, value); // Can be multiple authors for instance
     1474            // Refresh table
     1475            ((AttributeTableModel)element_attributes.getModel()).removeRow(current_attribute);
     1476            }
     1477            else if(current_set != null) {
     1478            String name = (String) set_attributes.getValueAt(current_attribute, 0);
     1479            // Remove set attribute
     1480            current_set.removeAttribute(name);
     1481            // Refresh table
     1482            ((AttributeTableModel)set_attributes.getModel()).removeRow(current_attribute);
     1483            }
     1484            // Disable buttons
     1485            edit_attribute.setEnabled(false);
     1486            remove_attribute.setEnabled(false);
     1487            ignore = false;
     1488        }
     1489        }
     1490    }
     1491    }
     1492
     1493    private class RemoveElementActionListener
     1494    implements ActionListener {
     1495    /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to provide an editing prompt.
     1496     * @param event An <strong>ActionEvent</strong> containing information about the event.
     1497     */
     1498    public void actionPerformed(ActionEvent event) {
     1499        if(current_element != null) {
     1500        int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("Element")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]);
     1501        // Remove this attribute
     1502        if(result == 0) {
     1503            ignore = true;
     1504            Gatherer.c_man.msm.removeElement(current_element);
     1505            // Clear selection
     1506            mds_tree.clearSelection();
     1507            model.remove(current_element.toString(), MEMNode.ELEMENT);
     1508            // Meanwhile disable/enable controls given we had an element selected, but no longer do.
     1509            remove_element.setEnabled(false);
     1510            // Show a blank panel.
     1511            card_layout.show(details_pane, BLANK);
     1512            ignore = false;
     1513        }
     1514        }
     1515        else {
     1516        ///ystem.err.println("No current element selected.");
     1517        }
     1518    }
     1519    }
     1520
     1521    private class RemoveFileActionListener
     1522    implements ActionListener {
     1523    /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to provide an editing prompt.
     1524     * @param event An <strong>ActionEvent</strong> containing information about the event.
     1525     */
     1526    public void actionPerformed(ActionEvent event) {
     1527        if(current_collection_file != null) {
     1528        int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("File")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]);
     1529        // Remove the current collection file profile from the profiler.
     1530        if(result == 0) {
     1531            ignore = true;
     1532            Gatherer.c_man.msm.profiler.removeProfile(current_collection_file);
     1533            // Clear selection
     1534            mds_tree.clearSelection();
     1535            model.remove(current_collection_file, MEMNode.COLLECTION);
     1536            // Meanwhile disable/enable controls given we had a set selected, but no longer do.
     1537            remove_file.setEnabled(false);
     1538            // Show a blank panel.
     1539            card_layout.show(details_pane, BLANK);
     1540            ignore = false;
     1541        }
     1542        }
     1543    }
     1544    }
     1545
     1546    private class RemoveSetActionListener
     1547    implements ActionListener {
     1548    /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to
     1549     * @param event An <strong>ActionEvent</strong> containing information about the event.
     1550     */
     1551    public void actionPerformed(ActionEvent event) {
     1552        if(current_set != null) {
     1553        int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("Set")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]);
     1554        // Remove the currently selected set
     1555        if(result == 0) {
     1556            ignore = true;
     1557            Gatherer.c_man.msm.removeSet(current_set);
     1558            // Clear selection
     1559            mds_tree.clearSelection();
     1560            model.remove(current_set.toString(), MEMNode.SET);
     1561            // Meanwhile disable/enable controls given we had a set selected, but no longer do.
     1562            remove_set.setEnabled(false);
     1563            // Show a blank panel.
     1564            card_layout.show(details_pane, BLANK);
     1565            ignore = false;
     1566        }
     1567        }
     1568    }
     1569    }
     1570    /** This class will remove the currently selected metadata or profile value when any registered control is actioned. Note that removing a value acts differently from the other removes in that if you remove a value which is still assigned somewhere the values are restored next time said assignment is viewed. This turned out far easier and reasonable to code than attempting to remove all remaining values when you delete its reference in the GValueModel. */
     1571    private class RemoveValueActionListener
     1572    implements ActionListener {
     1573    /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to
     1574     * @param event An <strong>ActionEvent</strong> containing information about the event.
     1575     */
     1576    public void actionPerformed(ActionEvent event) {
     1577        if(current_value_node != null) {                 
     1578        int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("Value")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]);
     1579        // Remove the current selected value
     1580        if(result == 0) {
     1581            ignore = true;
     1582            GValueModel model = (GValueModel) element_values.getModel();
     1583            model.removeValue(current_value_node);
     1584            // Meanwhile disable/enable controls given we had a value selected, but no longer do.
     1585            edit_value.setEnabled(false);
     1586            remove_value.setEnabled(false);
     1587            ignore = false;
     1588        }
     1589        }
     1590    }
     1591    }
    15921592     
    1593     /** Used to enable a certain target component if and only if all the registered text fields contain non-zero length strings. */
    1594     private class TextFieldEnabler
    1595         extends KeyAdapter {
    1596         private Component target = null;
    1597         private JTextComponent[] fields = null;
    1598         public TextFieldEnabler(Component target) {
    1599             super();
    1600             this.target = target;
    1601         }
    1602         public void add(JTextComponent field) {
    1603             field.addKeyListener(this);
    1604             if(fields == null) {
    1605                 fields = new JTextComponent[1];
    1606                 fields[0] = field;
     1593    /** Used to enable a certain target component if and only if all the registered text fields contain non-zero length strings. */
     1594    private class TextFieldEnabler
     1595    extends KeyAdapter {
     1596    private Component target = null;
     1597    private JTextComponent[] fields = null;
     1598    public TextFieldEnabler(Component target) {
     1599        super();
     1600        this.target = target;
     1601    }
     1602    public void add(JTextComponent field) {
     1603        field.addKeyListener(this);
     1604        if(fields == null) {
     1605        fields = new JTextComponent[1];
     1606        fields[0] = field;
     1607        }
     1608        else {
     1609        JTextComponent[] temp = new JTextComponent[fields.length + 1];
     1610        System.arraycopy(fields, 0, temp, 0, fields.length);
     1611        temp[fields.length] = field;
     1612        fields = temp;
     1613        }
     1614    }
     1615    public void keyReleased(KeyEvent event) {
     1616        boolean enable = true;
     1617        for(int i = 0; i < fields.length; i++) {
     1618        enable = enable && fields[i].getText().length() > 0;
     1619        }
     1620        target.setEnabled(enable);
     1621    }
     1622    }
     1623
     1624    private class AttributesListSelectionListener
     1625    implements ListSelectionListener {
     1626    private JTable table = null;
     1627    public AttributesListSelectionListener(JTable table) {
     1628        this.table = table;
     1629    }
     1630    public void valueChanged(ListSelectionEvent event) {
     1631        if((current_attribute = table.getSelectedRow()) != -1) {
     1632        edit_attribute.setEnabled(true);
     1633        remove_attribute.setEnabled(true);
     1634        }
     1635        else {
     1636        current_attribute = -1;
     1637        edit_attribute.setEnabled(false);
     1638        remove_attribute.setEnabled(false);
     1639        }
     1640    }
     1641    }
     1642
     1643    private class ElementValuesTreeSelectionListener
     1644    implements TreeSelectionListener {
     1645    public void valueChanged(TreeSelectionEvent event) {
     1646        if(element_values.getSelectionCount() > 0) {
     1647        // Retrieve the selected value node.
     1648        TreePath selected_path = element_values.getSelectionPath();
     1649        current_value_node = (GValueNode) selected_path.getLastPathComponent();
     1650        edit_value.setEnabled(true);
     1651        remove_value.setEnabled(true);
     1652        }
     1653        else {
     1654        current_value_node = null;
     1655        edit_value.setEnabled(false);
     1656        remove_value.setEnabled(false);
     1657        }
     1658    }
     1659    }
     1660
     1661    private class MDSTreeSelectionListener
     1662    implements TreeSelectionListener {
     1663    public void valueChanged(TreeSelectionEvent event) {
     1664        if(!ignore) {
     1665        // Clear all variables based on previous tree selection
     1666        current_collection_file = null;
     1667        current_element = null;
     1668        current_set = null;
     1669        // Now process the node selected
     1670        TreePath path = event.getPath();
     1671        current_node = (MEMNode)path.getLastPathComponent();
     1672        // What we show depends on the node type...
     1673        AttributeTableModel atm = null;
     1674        TreeSet attributes = null;
     1675        switch(current_node.getType()) {
     1676        case MEMNode.COLLECTION:
     1677            current_collection_file = current_node.toString();
     1678            atm = current_node.getModel();
     1679            if(atm == null) {
     1680            ArrayList sources = Gatherer.c_man.msm.profiler.getSources(current_collection_file);
     1681            attributes = new TreeSet();
     1682            for(int i = 0; i < sources.size(); i++) {
     1683                String source = (String) sources.get(i);
     1684                String action = Gatherer.c_man.msm.profiler.getAction(current_collection_file, source);
     1685                if(action == null) {
     1686                action = get("Ignore");
     1687                }
     1688                attributes.add(new Attribute(source, action));
    16071689            }
    1608             else {
    1609                 JTextComponent[] temp = new JTextComponent[fields.length + 1];
    1610                 System.arraycopy(fields, 0, temp, 0, fields.length);
    1611                 temp[fields.length] = field;
    1612                 fields = temp;
     1690            atm = new AttributeTableModel(attributes, get("Source"), get("Target"), get("Ignore"));
     1691            //current_node.setModel(atm);
     1692            }
     1693            profile_attributes.setModel(atm);
     1694            atm.setScrollPane(profile_attributes_scroll);
     1695            atm.setTable(profile_attributes);
     1696
     1697            card_layout.show(details_pane, PROFILE);
     1698            setControls(true, false, false, true, false, false, true, false, false, false, false, false);
     1699            break;
     1700        case MEMNode.ELEMENT:
     1701            current_element = current_node.getElement();
     1702            atm = current_node.getModel();
     1703            if(atm == null) {
     1704            atm = new AttributeTableModel(current_element.getAttributes(), get("Name"), get("Language_Code"), get("Value"), "");
     1705            //current_node.setModel(atm);
     1706            }
     1707            element_attributes.setModel(atm);
     1708            atm.setScrollPane(element_attributes_scroll);
     1709            atm.setTable(element_attributes);
     1710
     1711            element_values.setModel(Gatherer.c_man.msm.getValueTree(current_element));
     1712            card_layout.show(details_pane, ELEMENT);
     1713            // Meanwhile disable/enable controls depending on this Element selection.
     1714            setControls(true, false, false, false, false, true, true, false, false, true, false, false);
     1715            break;
     1716        case MEMNode.SET:
     1717            current_set = current_node.getSet();
     1718            atm = current_node.getModel();
     1719            if(atm == null) {
     1720            NamedNodeMap temp = current_set.getAttributes();
     1721            attributes = new TreeSet();
     1722            for(int i = 0; i < temp.getLength(); i++) {
     1723                Attr attribute = (Attr) temp.item(i);
     1724                // We don't show the namespace attribute, as it is used as a unique primary key and should never be changed or removed in itself.
     1725                if(!attribute.getName().equals("namespace")) {
     1726                attributes.add(new Attribute(attribute.getName(), attribute.getValue()));
     1727                }
     1728                attribute = null;
    16131729            }
    1614         }
    1615         public void keyReleased(KeyEvent event) {
    1616             boolean enable = true;
    1617             for(int i = 0; i < fields.length; i++) {
    1618                 enable = enable && fields[i].getText().length() > 0;
    1619             }
    1620             target.setEnabled(enable);
    1621         }
    1622     }
    1623 
    1624     private class AttributesListSelectionListener
    1625         implements ListSelectionListener {
    1626          private JTable table = null;
    1627          public AttributesListSelectionListener(JTable table) {
    1628               this.table = table;
    1629          }
    1630          public void valueChanged(ListSelectionEvent event) {
    1631               if((current_attribute = table.getSelectedRow()) != -1) {
    1632                     edit_attribute.setEnabled(true);
    1633                     remove_attribute.setEnabled(true);
    1634               }
    1635               else {
    1636                     current_attribute = -1;
    1637                     edit_attribute.setEnabled(false);
    1638                     remove_attribute.setEnabled(false);
    1639               }
    1640          }
    1641     }
    1642 
    1643     private class ElementValuesTreeSelectionListener
    1644         implements TreeSelectionListener {
    1645          public void valueChanged(TreeSelectionEvent event) {
    1646               if(element_values.getSelectionCount() > 0) {
    1647                     // Retrieve the selected value node.
    1648                     TreePath selected_path = element_values.getSelectionPath();
    1649                     current_value_node = (GValueNode) selected_path.getLastPathComponent();
    1650                     edit_value.setEnabled(true);
    1651                     remove_value.setEnabled(true);
    1652               }
    1653               else {
    1654                     current_value_node = null;
    1655                     edit_value.setEnabled(false);
    1656                     remove_value.setEnabled(false);
    1657               }
    1658          }
    1659     }
    1660 
    1661     private class MDSTreeSelectionListener
    1662         implements TreeSelectionListener {
    1663          public void valueChanged(TreeSelectionEvent event) {
    1664               if(!ignore) {
    1665                     // Clear all variables based on previous tree selection
    1666                     current_collection_file = null;
    1667                     current_element = null;
    1668                     current_set = null;
    1669                     // Now process the node selected
    1670                     TreePath path = event.getPath();
    1671                     current_node = (MEMNode)path.getLastPathComponent();
    1672                     // What we show depends on the node type...
    1673                     AttributeTableModel atm = null;
    1674                     TreeSet attributes = null;
    1675                     switch(current_node.getType()) {
    1676                     case MEMNode.COLLECTION:
    1677                          current_collection_file = current_node.toString();
    1678                          atm = current_node.getModel();
    1679                          if(atm == null) {
    1680                               ArrayList sources = Gatherer.c_man.msm.profiler.getSources(current_collection_file);
    1681                               attributes = new TreeSet();
    1682                               for(int i = 0; i < sources.size(); i++) {
    1683                                     String source = (String) sources.get(i);
    1684                                     String action = Gatherer.c_man.msm.profiler.getAction(current_collection_file, source);
    1685                                     if(action == null) {
    1686                                          action = get("Ignore");
    1687                                     }
    1688                                     attributes.add(new Attribute(source, action));
    1689                               }
    1690                               atm = new AttributeTableModel(attributes, get("Source"), get("Target"), get("Ignore"));
    1691                               //current_node.setModel(atm);
    1692                          }
    1693                          profile_attributes.setModel(atm);
    1694                          atm.setScrollPane(profile_attributes_scroll);
    1695                          atm.setTable(profile_attributes);
    1696 
    1697                          card_layout.show(details_pane, PROFILE);
    1698                          setControls(true, false, false, true, false, false, true, false, false, false, false, false);
    1699                          break;
    1700                     case MEMNode.ELEMENT:
    1701                          current_element = current_node.getElement();
    1702                          atm = current_node.getModel();
    1703                          if(atm == null) {
    1704                               atm = new AttributeTableModel(current_element.getAttributes(), get("Name"), get("Language_Code"), get("Value"), "");
    1705                               //current_node.setModel(atm);
    1706                          }
    1707                          element_attributes.setModel(atm);
    1708                          atm.setScrollPane(element_attributes_scroll);
    1709                          atm.setTable(element_attributes);
    1710 
    1711                          element_values.setModel(Gatherer.c_man.msm.getValueTree(current_element));
    1712                          card_layout.show(details_pane, ELEMENT);
    1713                          // Meanwhile disable/enable controls depending on this Element selection.
    1714                          setControls(true, false, false, false, false, true, true, false, false, true, false, false);
    1715                          break;
    1716                     case MEMNode.SET:
    1717                          current_set = current_node.getSet();
    1718                          atm = current_node.getModel();
    1719                          if(atm == null) {
    1720                               NamedNodeMap temp = current_set.getAttributes();
    1721                               attributes = new TreeSet();
    1722                               for(int i = 0; i < temp.getLength(); i++) {
    1723                                     Attr attribute = (Attr) temp.item(i);
    1724                                     // We don't show the namespace attribute, as it is used as a unique primary key and should never be changed or removed in itself.
    1725                                     if(!attribute.getName().equals("namespace")) {
    1726                                          attributes.add(new Attribute(attribute.getName(), attribute.getValue()));
    1727                                     }
    1728                                     attribute = null;
    1729                               }
    1730                               atm = new AttributeTableModel(attributes, get("Name"), get("Value"), "");
    1731                               //current_node.setModel(atm);
    1732                               temp = null;
    1733                          }
    1734                          set_attributes.setModel(atm);
    1735                          atm.setScrollPane(set_attributes_scroll);
    1736                          atm.setTable(set_attributes);
     1730            atm = new AttributeTableModel(attributes, get("Name"), get("Value"), "");
     1731            //current_node.setModel(atm);
     1732            temp = null;
     1733            }
     1734            set_attributes.setModel(atm);
     1735            atm.setScrollPane(set_attributes_scroll);
     1736            atm.setTable(set_attributes);
    17371737                         
    1738                         card_layout.show(details_pane, SET);
    1739                         attributes = null;
    1740                         // Meanwhile disable/enable controls depending on this Element selection.
    1741                         setControls(true, true, false, false, true, false, true, false, false, false, false, false);
    1742                         break;
    1743                     case MEMNode.PROFILER:
    1744                         // Meanwhile disable/enable controls depending on this Element selection.
    1745                         setControls(true, false, true, false, false, false, false, false, false, false, false, false);
    1746                     default:
    1747                         // Show a blank panel.
    1748                         card_layout.show(details_pane, BLANK);
    1749                     }
    1750                     attributes = null;
    1751                     path = null;
    1752                     atm = null;
    1753               }
    1754          }
    1755     }
    1756 
    1757     private class MEMTreeCellRenderer
    1758           extends DefaultTreeCellRenderer {
    1759           public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
    1760                 super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
    1761                 setToolTipText(value.toString());
    1762                 return this;
    1763           }
    1764     }
    1765 
    1766     /** Wrap a ElementWrapper so we get its name when we ask for toString(), not its identifier. */
    1767     private class NameElementWrapperEntry
    1768           implements Comparable {
    1769           private ElementWrapper element_wrapper = null;
    1770           public NameElementWrapperEntry(Object object) {
    1771                 this.element_wrapper = (ElementWrapper) object;
    1772           }
    1773           public int compareTo(Object object) {
    1774                 return element_wrapper.compareTo(object);
    1775           }
    1776           public boolean equals(Object object) {
    1777                 return element_wrapper.equals(object);
    1778           }
    1779           public String toString() {
    1780                 return element_wrapper.getName();
    1781           }
    1782     }
     1738            card_layout.show(details_pane, SET);
     1739            attributes = null;
     1740            // Meanwhile disable/enable controls depending on this Element selection.
     1741            setControls(true, true, false, false, true, false, true, false, false, false, false, false);
     1742            break;
     1743        case MEMNode.PROFILER:
     1744            // Meanwhile disable/enable controls depending on this Element selection.
     1745            setControls(true, false, true, false, false, false, false, false, false, false, false, false);
     1746        default:
     1747            // Show a blank panel.
     1748            card_layout.show(details_pane, BLANK);
     1749        }
     1750        attributes = null;
     1751        path = null;
     1752        atm = null;
     1753        }
     1754    }
     1755    }
     1756
     1757    private class MEMTreeCellRenderer
     1758    extends DefaultTreeCellRenderer {
     1759    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
     1760        super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
     1761        setToolTipText(value.toString());
     1762        return this;
     1763    }
     1764    }
     1765
     1766    /** Wrap a ElementWrapper so we get its name when we ask for toString(), not its identifier. */
     1767    private class NameElementWrapperEntry
     1768    implements Comparable {
     1769    private ElementWrapper element_wrapper = null;
     1770    public NameElementWrapperEntry(Object object) {
     1771        this.element_wrapper = (ElementWrapper) object;
     1772    }
     1773    public int compareTo(Object object) {
     1774        return element_wrapper.compareTo(object);
     1775    }
     1776    public boolean equals(Object object) {
     1777        return element_wrapper.equals(object);
     1778    }
     1779    public String toString() {
     1780        return element_wrapper.getName();
     1781    }
     1782    }
    17831783}
    1784 
  • trunk/gli/src/org/greenstone/gatherer/msm/Declarations.java

    r4293 r4365  
    5555 */
    5656final public class Declarations {
    57     /** An element of the action type enumeration. */
    58     static public final int NO_ACTION   = 0;
    59     /** An element of the action type enumeration. */
    60     static public final int ADD         = 1;
    61     /** An element of the action type enumeration. */
    62     static public final int CANCEL      = 2;
    63     /** An element of the action type enumeration. This action merges two unrelated elements, as requested by the user. */
    64     static public final int FORCE_MERGE = 3;
    65     /** An element of the action type enumeration. */
    66     static public final int MERGE       = 4;
    67     /** An element of the action type enumeration. */
    68     static public final int RENAME      = 5;
    69     /** An element of the action type enumeration. */
    70     static public final int REPLACE     = 6;
    71     /** An element of the action type enumeration. */
    72     static public final int SKIP        = 7;
    73     /** An element of the action type enumeration. */
    74     static public final int HFILE       = 8;
    75     /** An element of the action type enumeration. This special value is used internally to indicate a certain element has been removed from the metadata sets. */
    76     static public final int DELETE      = 9;
     57    /** An element of the action type enumeration. */
     58    static public final int NO_ACTION   = 0;
     59    /** An element of the action type enumeration. */
     60    static public final int ADD         = 1;
     61    /** An element of the action type enumeration. */
     62    static public final int CANCEL      = 2;
     63    /** An element of the action type enumeration. This action merges two unrelated elements, as requested by the user. */
     64    static public final int FORCE_MERGE = 3;
     65    /** An element of the action type enumeration. */
     66    static public final int MERGE       = 4;
     67    /** An element of the action type enumeration. */
     68    static public final int RENAME      = 5;
     69    /** An element of the action type enumeration. */
     70    static public final int REPLACE     = 6;
     71    /** An element of the action type enumeration. */
     72    static public final int SKIP        = 7;
     73    /** An element of the action type enumeration. */
     74    static public final int HFILE       = 8;
     75    /** An element of the action type enumeration. This special value is used internally to indicate a certain element has been removed from the metadata sets. */
     76    static public final int DELETE      = 9;
    7777}
  • trunk/gli/src/org/greenstone/gatherer/msm/ElementWrapper.java

    r4314 r4365  
    6060 */
    6161public class ElementWrapper
    62     implements Comparable {
    63     /** The DOM element this wrapper is wrapped around. */
    64     private Element element = null;
    65     /** A string prefix identifying the metadata set namespace. */
    66     private String namespace = null;
    67     /** Constructor for elements with no namespace necessary.
    68       * @param element The DOM <strong>Element</strong> this is to be based on.
     62    implements Comparable {
     63    /** The DOM element this wrapper is wrapped around. */
     64    private Element element = null;
     65    /** A string prefix identifying the metadata set namespace. */
     66    private String namespace = null;
     67    /** Constructor for elements with no namespace necessary.
     68     * @param element The DOM <strong>Element</strong> this is to be based on.
    6969      */
    70     public ElementWrapper(Element element) {
    71           this.element = element;
    72     }
    73     /** Constructor.
     70    public ElementWrapper(Element element) {
     71    this.element = element;
     72    }
     73    /** Constructor.
    7474      * @param element The DOM <strong>Element</strong> this is to be based on.
    7575      * @param namespace_required <i>true</i> if this element wrapper should always attempt to show a namespace prefix (retrieving it from the DOM if necessary).
    7676      * @deprecated
    7777      */
    78     public ElementWrapper(Element element, boolean namespace_required) {
    79           this(element);
    80     }
    81 
    82     public void addAttribute(String name, String value) {
    83           MSMUtils.addElementAttribute(element, name, "en", value);
    84     }
    85 
    86     public void addAttribute(String name, String language, String value) {
    87           MSMUtils.addElementAttribute(element, name, language, value);
    88     }
    89 
    90     /** Create a copy of this element wrapper.
     78    public ElementWrapper(Element element, boolean namespace_required) {
     79    this(element);
     80    }
     81
     82    public void addAttribute(String name, String value) {
     83    MSMUtils.addElementAttribute(element, name, "en", value);
     84    }
     85
     86    public void addAttribute(String name, String language, String value) {
     87    MSMUtils.addElementAttribute(element, name, language, value);
     88    }
     89
     90    /** Create a copy of this element wrapper.
    9191      * @return A new <strong>ElementWrapper</strong> based on the same element as this one.
    9292      */
    93     public ElementWrapper copy() {
    94           Element element_copy = (Element) element.cloneNode(true);
    95           return new ElementWrapper(element_copy);
    96     }
    97     /** Compare two element wrappers for ordering.
     93    public ElementWrapper copy() {
     94    Element element_copy = (Element) element.cloneNode(true);
     95    return new ElementWrapper(element_copy);
     96    }
     97    /** Compare two element wrappers for ordering.
    9898      * @param object An <strong>Object</strong> which is most likely another element wrapper to be compared with.
    9999      * @return An <i>int</i> indicating order, -1, 0, 1 if this element wrapper is less than, equal to or greater than the given object.
    100100      */
    101     public int compareTo(Object object) {
    102           return toString().compareTo(object.toString());
    103     }
    104     /** Decrement the number of occurances of this metadata element.
    105       * @see org.greenstone.gatherer.msm.MSMUtils
    106       */
    107     public void dec() {
    108           MSMUtils.setOccurance(element, -1);
    109     }
    110     /** Test if two ElementWrappers are equal.
     101    public int compareTo(Object object) {
     102    return toString().compareTo(object.toString());
     103    }
     104    /** Decrement the number of occurances of this metadata element.
     105      * @see org.greenstone.gatherer.msm.MSMUtils
     106      */
     107    public void dec() {
     108    MSMUtils.setOccurance(element, -1);
     109    }
     110    /** Test if two ElementWrappers are equal.
    111111      * @param object The <strong>Object</strong> to test against.
    112112      */
    113     public boolean equals(Object object) {
    114           if(object instanceof ElementWrapper) {
    115                 String our_full_name = MSMUtils.getFullName(element);
    116                 String their_full_name = MSMUtils.getFullName(((ElementWrapper)object).getElement());
    117                 return our_full_name.equals(their_full_name);
    118           }
    119           return toString().equals(object.toString());
    120     }
    121     /** Retrieve the attributes associated with the element this element wrapper is built around.
     113    public boolean equals(Object object) {
     114    if(object instanceof ElementWrapper) {
     115        String our_full_name = MSMUtils.getFullName(element);
     116        String their_full_name = MSMUtils.getFullName(((ElementWrapper)object).getElement());
     117        return our_full_name.equals(their_full_name);
     118    }
     119    return toString().equals(object.toString());
     120    }
     121    /** Retrieve the attributes associated with the element this element wrapper is built around.
    122122      * @return A <strong>TreeSet</strong> of the attributes.
    123123      * @see org.greenstone.gatherer.msm.MSMUtils
    124124      */
    125     public TreeSet getAttributes() {
    126           return MSMUtils.getAttributes(element);
    127     }
    128     /** Retrieve the element this is wrapped around.
     125    public TreeSet getAttributes() {
     126    return MSMUtils.getAttributes(element);
     127    }
     128    /** Retrieve the element this is wrapped around.
    129129      * @return A DOM <strong>Element</strong> which represents a metadata element.
    130130      */
    131     public Element getElement() {
    132           return element;
    133     }
    134     /** Retrieve the identity of this element (not necessary the same as this elements name). Identity is language and locale dependant.
     131    public Element getElement() {
     132    return element;
     133    }
     134    /** Retrieve the identity of this element (not necessary the same as this elements name). Identity is language and locale dependant.
    135135      * @return The identity as a <strong>String</strong>.
    136136      * @see org.greenstone.gatherer.msm.MSMUtils
    137137      */
    138     public String getIdentity() {
    139           return MSMUtils.getIdentifier(element);
    140     }
    141     /** Retrieve the name of this element. Name is unique.
     138    public String getIdentity() {
     139    return MSMUtils.getIdentifier(element);
     140    }
     141    /** Retrieve the name of this element. Name is unique.
    142142      * @return The name as a <strong>String</strong>.
    143143      * @see org.greenstone.gatherer.msm.MSMUtils
    144144      */
    145     public String getName() {
    146           return MSMUtils.getFullName(element);
    147     }
    148     /** Retrieve the namespace prefix for this element wrapper.
     145    public String getName() {
     146    return MSMUtils.getFullName(element);
     147    }
     148    /** Retrieve the namespace prefix for this element wrapper.
    149149      * @return A <strong>String</strong> containing the namespace or "" if there is no namespace for this element.
    150150      * @see org.greenstone.gatherer.msm.MSMUtils
    151151      */
    152     public String getNamespace() {
    153           String name = getName();
    154           int pos;
    155           if((pos = name.indexOf(MSMUtils.NS_SEP)) != -1) {
    156                 return name.substring(0, pos);
    157           }
    158           else {
    159                 return "";
    160           }
    161     }
    162     /** Look for the occurances 'field' of the element and return it if found.
     152    public String getNamespace() {
     153    String name = getName();
     154    int pos;
     155    if((pos = name.indexOf(MSMUtils.NS_SEP)) != -1) {
     156        return name.substring(0, pos);
     157    }
     158    else {
     159        return "";
     160    }
     161    }
     162    /** Look for the occurances 'field' of the element and return it if found.
    163163      * @return An <i>int</i> which matches the number in the occurances attribute of the element, or 0 if no such attribute.
    164164      * @see org.greenstone.gatherer.msm.MSMUtils
    165165      */
    166     public int getOccurances() {
    167           return MSMUtils.getOccurances(element);
    168     }
    169     /** This method is essentially the same as getDescription() in that it does indeed return this metaelements description. However this method uses the Utility function formatHTMLWidth() to ensure the String can be displayed in a tool-tip window using html markup.
     166    public int getOccurances() {
     167    return MSMUtils.getOccurances(element);
     168    }
     169    /** This method is essentially the same as getDescription() in that it does indeed return this metaelements description. However this method uses the Utility function formatHTMLWidth() to ensure the String can be displayed in a tool-tip window using html markup.
    170170      * @return A String containing the HTML formatted versions of definition and content (comment).
    171171      * @see org.greenstone.gatherer.msm.MSMUtils
    172172      * @see org.greenstone.gatherer.util.Utility
    173173      */
    174     public String getToolTip() {
    175           // Add HTML formatting
    176           return Utility.formatHTMLWidth(MSMUtils.getDescription(element), 60);
    177     }
    178     /** Increment the number of occurances of this metadata element.
    179       * @see org.greenstone.gatherer.msm.MSMUtils
    180       */
    181     public void inc() {
    182           MSMUtils.setOccurance(element, 1);
    183     }
    184 
    185     public boolean isHierarchy() {
    186           return element.getAttribute("hierarchy").equalsIgnoreCase("true");
    187     }
    188 
    189     public void removeAttribute(String name, String value) {
    190           Element attributes[] = MSMUtils.getAttributeNodesNamed(element, name);
    191           // For each of the attribute nodes which match the requested name...
    192           for(int i = 0; attributes != null && i < attributes.length; i++) {
     174    public String getToolTip() {
     175    // Add HTML formatting
     176    return Utility.formatHTMLWidth(MSMUtils.getDescription(element), 60);
     177    }
     178    /** Increment the number of occurances of this metadata element.
     179      * @see org.greenstone.gatherer.msm.MSMUtils
     180      */
     181    public void inc() {
     182    MSMUtils.setOccurance(element, 1);
     183    }
     184
     185    public boolean isHierarchy() {
     186    return element.getAttribute("hierarchy").equalsIgnoreCase("true");
     187    }
     188
     189    public void removeAttribute(String name, String value) {
     190    Element attributes[] = MSMUtils.getAttributeNodesNamed(element, name);
     191    // For each of the attribute nodes which match the requested name...
     192    for(int i = 0; attributes != null && i < attributes.length; i++) {
    193193                // Retrieve the value for this node...
    194                 String current = MSMUtils.getValue(attributes[i]);
     194        String current = MSMUtils.getValue(attributes[i]);
    195195                // And if it matches our removal value, remove the attribute node.
    196                 if(current.equals(value)) {
    197                      element.removeChild(attributes[i]);
    198                      current = null;
    199                      attributes = null;
    200                      return;
    201                 }
    202                 current = null;
    203           }
    204           // If we get this far then theres no match. Boo-hoo.
    205           attributes = null;
    206     }
    207     /** Set the value of the namespace required flag.
     196        if(current.equals(value)) {
     197        element.removeChild(attributes[i]);
     198        current = null;
     199        attributes = null;
     200        return;
     201        }
     202        current = null;
     203    }
     204    // If we get this far then theres no match. Boo-hoo.
     205    attributes = null;
     206    }
     207    /** Set the value of the namespace required flag.
    208208      * @param namespace_required The new value as a <i>boolean</i>.
    209209      * @see org.greenstone.gatherer.msm.MSMUtils
    210210      * @deprecated
    211211      */
    212     public void setNamespaceRequired(boolean namespace_required) {
    213     }
    214     public String toString() {
    215           return MSMUtils.getFullIdentifier(element);
    216     }
     212    public void setNamespaceRequired(boolean namespace_required) {
     213    }
     214    public String toString() {
     215    return MSMUtils.getFullIdentifier(element);
     216    }
    217217}
    218 
    219 
  • trunk/gli/src/org/greenstone/gatherer/msm/ExistingMetadataLoader.java

    r4293 r4365  
    4848 */
    4949public class ExistingMetadataLoader {
    50     /** A list of currently loaded metadata parsers. */
    51     private ArrayList parsers = null;
    52     /** Constructor.
    53       * @see org.greenstone.gatherer.msm.MetadataParser
    54       * @see org.greenstone.gatherer.util.Utility
    55       */
    56     public ExistingMetadataLoader() {
    57           this.parsers = new ArrayList();
    58           ArrayList load_these = new ArrayList();
    59           // Find any metadata parsers found in parsers directory
    60           File file = new File(Utility.BASE_DIR + "classes" + File.separator + "org" + File.separator + "greenstone" + File.separator + "gatherer" + File.separator + "msm" + File.separator + "parsers");
    61           File children[] = file.listFiles();
    62           for(int i = 0; children != null && i < children.length; i++) {
    63                 String name = children[i].getName();
    64                 if(name.endsWith(".class") && name.indexOf("$") == -1) {
    65                      name = name.substring(0, name.length() - 6);
    66                      load_these.add("org.greenstone.gatherer.msm.parsers." + name);
    67                      Gatherer.println("Loaded metadata parser: " + name);
    68                 }
    69                 name = null;
    70           }
    71           children = null;
    72           file = null;
    73           // Find any metadata parsers found in the jar file (if present)
    74           File jar_file = new File("Gatherer.jar");
    75           if(jar_file.exists()) {
    76                 try {
    77                      JarFile jar = new JarFile(jar_file);
    78                      for(Enumeration entries = jar.entries(); entries.hasMoreElements(); ) {
    79                           String name = entries.nextElement().toString();
    80                           if(name.startsWith("org/greenstone/gatherer/msm/parsers/") && name.endsWith(".class") && name.indexOf("$") == -1) {
    81                                 name = name.substring(0, name.length() - 6);
    82                                 name = name.replace('/', '.');
    83                                 if(!load_these.contains(name)) {
    84                                     load_these.add(name);
    85                                     Gatherer.println("Loaded metadata parser: " + name);
    86                                 }
    87                           }                     
    88                           name = null;
    89                      }
    90                      jar = null;
    91                 }
    92                 catch (Exception error) {
    93                      error.printStackTrace();
    94                 }
    95           }
    96           jar_file = null;
    97           // Load create instances of the parsers found.
    98           for(int i = 0; i < load_these.size(); i++) {
    99                 try {
    100                      String parser_name = (String) load_these.get(i);
    101                      Class custom_class = Class.forName((String)load_these.get(i));
    102                      MetadataParser custom_parser = (MetadataParser)custom_class.newInstance();
    103                      parsers.add(custom_parser);
    104                      custom_parser = null;
    105                      custom_class = null;
    106                      parser_name = null;
    107                 }
    108                 catch (Exception error) {
    109                      error.printStackTrace();
    110                 }
    111           }
    112     }
    113     /** Locates and assigns metadata for the given file by sequentially applying all active metadata parsers. */
    114     public boolean searchForMetadata(FileNode destination, FileNode source, boolean folder_level, boolean dummy_run) {
    115           boolean dialog_cancelled = false;
    116           int size = parsers.size();
    117           for(int i = 0; !dialog_cancelled && i < size; i++) {
    118                 MetadataParser parser = (MetadataParser) parsers.get(i);
    119                 dialog_cancelled = parser.process(destination, source, folder_level, dummy_run);
    120                 parser = null;
    121           }
    122           return dialog_cancelled;
    123     }
     50    /** A list of currently loaded metadata parsers. */
     51    private ArrayList parsers = null;
     52    /** Constructor.
     53     * @see org.greenstone.gatherer.msm.MetadataParser
     54     * @see org.greenstone.gatherer.util.Utility
     55     */
     56    public ExistingMetadataLoader() {
     57    this.parsers = new ArrayList();
     58    ArrayList load_these = new ArrayList();
     59    // Find any metadata parsers found in parsers directory
     60    File file = new File(Utility.BASE_DIR + "classes" + File.separator + "org" + File.separator + "greenstone" + File.separator + "gatherer" + File.separator + "msm" + File.separator + "parsers");
     61    File children[] = file.listFiles();
     62    for(int i = 0; children != null && i < children.length; i++) {
     63        String name = children[i].getName();
     64        if(name.endsWith(".class") && name.indexOf("$") == -1) {
     65        name = name.substring(0, name.length() - 6);
     66        load_these.add("org.greenstone.gatherer.msm.parsers." + name);
     67        Gatherer.println("Loaded metadata parser: " + name);
     68        }
     69        name = null;
     70    }
     71    children = null;
     72    file = null;
     73    // Find any metadata parsers found in the jar file (if present)
     74    File jar_file = new File("Gatherer.jar");
     75    if(jar_file.exists()) {
     76        try {
     77        JarFile jar = new JarFile(jar_file);
     78        for(Enumeration entries = jar.entries(); entries.hasMoreElements(); ) {
     79            String name = entries.nextElement().toString();
     80            if(name.startsWith("org/greenstone/gatherer/msm/parsers/") && name.endsWith(".class") && name.indexOf("$") == -1) {
     81            name = name.substring(0, name.length() - 6);
     82            name = name.replace('/', '.');
     83            if(!load_these.contains(name)) {
     84                load_these.add(name);
     85                Gatherer.println("Loaded metadata parser: " + name);
     86            }
     87            }                       
     88            name = null;
     89        }
     90        jar = null;
     91        }
     92        catch (Exception error) {
     93        error.printStackTrace();
     94        }
     95    }
     96    jar_file = null;
     97    // Load create instances of the parsers found.
     98    for(int i = 0; i < load_these.size(); i++) {
     99        try {
     100        String parser_name = (String) load_these.get(i);
     101        Class custom_class = Class.forName((String)load_these.get(i));
     102        MetadataParser custom_parser = (MetadataParser)custom_class.newInstance();
     103        parsers.add(custom_parser);
     104        custom_parser = null;
     105        custom_class = null;
     106        parser_name = null;
     107        }
     108        catch (Exception error) {
     109        error.printStackTrace();
     110        }
     111    }
     112    }
     113    /** Locates and assigns metadata for the given file by sequentially applying all active metadata parsers. */
     114    public boolean searchForMetadata(FileNode destination, FileNode source, boolean folder_level, boolean dummy_run) {
     115    boolean dialog_cancelled = false;
     116    int size = parsers.size();
     117    for(int i = 0; !dialog_cancelled && i < size; i++) {
     118        MetadataParser parser = (MetadataParser) parsers.get(i);
     119        dialog_cancelled = parser.process(destination, source, folder_level, dummy_run);
     120        parser = null;
     121    }
     122    return dialog_cancelled;
     123    }
    124124}
    125 
    126 
  • trunk/gli/src/org/greenstone/gatherer/msm/ExportMDSPrompt.java

    r4293 r4365  
    5050 */
    5151final public class ExportMDSPrompt
    52     extends JDialog
    53     implements ActionListener, KeyListener {
    54     /** Is this an export prompt or an import one? */
    55     private boolean export;
    56     /** The file we wish to export the metadata set to. */
    57     private File file = null;
    58     /** The action the user has chosen from the dialog (either -1, if cancelled, or EXPORT). */
    59     private int action = -1;
    60     /** The button used to browse the local file system. */
    61     private JButton browse_button = null;
    62     /** Used to cancel the dialog. */
    63     private JButton cancel_button = null;
    64     /** Used to initiate the export, then dispose of the dialog. */
    65     private JButton export_button = null;
    66     /** The metadata sets available for export. */
    67     private JComboBox sets = null;
    68     /** The destination file name as a string. */
    69     private JTextField file_name = null;
    70     /** Ths button that, if selected, signifies you wish to export the metadata set with all values. */
    71     private JToggleButton all_values = null;
    72     /** The button that, if selected, signifies you wish to export the metadata set without any values (ie no mdv files). */
    73     private JToggleButton no_values = null;
    74     /** The button that, if selected, signifies you wish to export the metadata set with only those values that are subject nodes in the hierarchy. */
    75     private JToggleButton structure_only = null;
    76     /** A reference to the metadata set manager. */
    77     private MetadataSetManager manager;
    78     /** The default size for this dialog window. */
    79     final static public Dimension EXPORT_SIZE = new Dimension(500,220);
    80 /** The default size for this dialog window. */
    81     final static public Dimension IMPORT_SIZE = new Dimension(500,120);
    82     /** The default export action (there are several depending on how much information you wish to export). */
    83     final static public int EXPORT = 0;
    84     /** Constructor.
    85       * @param manager A reference to the <strong>MetadataSetManager</strong>.
    86       * @see org.greenstone.gatherer.Configuration
    87       * @see org.greenstone.gatherer.util.Utility
    88       */
    89     public ExportMDSPrompt(MetadataSetManager manager, boolean export) {
    90           super();
    91           this.export = export;
    92           // Creation
    93           setModal(true);
    94           if(export) {
    95                 setSize(EXPORT_SIZE);
    96                 setTitle(get("Export_Title"));
    97           }
    98           else {
    99                 setSize(IMPORT_SIZE);
    100                 setTitle(get("Import_Title"));
    101           }
    102           JPanel content_pane = (JPanel) getContentPane();
    103           JPanel control_pane = new JPanel();
    104           JLabel set_label = new JLabel(get("Export_Set"));
    105           sets = new JComboBox(manager.getSets());
    106           JLabel condition_label = new JLabel(get("Export_Conditions"));
    107           JPanel condition_pane = new JPanel();
    108           ButtonGroup condition_group = new ButtonGroup();
    109           all_values = new JToggleButton(get("Export_All_Values"));
    110           condition_group.add(all_values);
    111           no_values = new JToggleButton(get("Export_No_Values"));
    112           condition_group.add(no_values);
    113           structure_only = new JToggleButton(get("Export_Subjects_Only"));
    114           condition_group.add(structure_only);
    115           all_values.setSelected(true);
    116           JLabel file_label = new JLabel(get("Export_File"));
    117           JPanel file_pane = new JPanel();
    118           file_name = new JTextField(Utility.METADATA_DIR);
    119           browse_button = new JButton(get("General.Browse"));
    120           JPanel button_pane = new JPanel();
    121           if(export) {
    122                 export_button = new JButton(get("File_Export"));
    123                 export_button.setEnabled(false);
    124           }
    125           else {
    126                 export_button = new JButton(get("File_Import"));
    127                 export_button.setEnabled(true);
    128           }
    129           cancel_button = new JButton(get("General.Cancel"));
    130           // Listeners
    131           browse_button.addActionListener(this);
    132           cancel_button.addActionListener(this);
    133           export_button.addActionListener(this);
    134           file_name.addKeyListener(this);
    135           // Layout
    136           condition_pane.setLayout(new GridLayout(1,3));
    137           condition_pane.add(all_values);
    138           condition_pane.add(structure_only);
    139           condition_pane.add(no_values);
    140 
    141           file_pane.setLayout(new BorderLayout());
    142           file_pane.add(file_name, BorderLayout.CENTER);
    143           file_pane.add(browse_button, BorderLayout.EAST);
    144 
    145           if(export) {
    146                 control_pane.setLayout(new GridLayout(6, 1));
    147                 control_pane.add(set_label);
    148                 control_pane.add(sets);
    149                 control_pane.add(condition_label);
    150                 control_pane.add(condition_pane);
    151                 control_pane.add(file_label);
    152                 control_pane.add(file_pane);
    153           }
    154           else {
    155                 control_pane.setLayout(new GridLayout(2,1));
    156                 control_pane.add(condition_label);
    157                 control_pane.add(condition_pane);
    158           }
     52    extends JDialog
     53    implements ActionListener, KeyListener {
     54    /** Is this an export prompt or an import one? */
     55    private boolean export;
     56    /** The file we wish to export the metadata set to. */
     57    private File file = null;
     58    /** The action the user has chosen from the dialog (either -1, if cancelled, or EXPORT). */
     59    private int action = -1;
     60    /** The button used to browse the local file system. */
     61    private JButton browse_button = null;
     62    /** Used to cancel the dialog. */
     63    private JButton cancel_button = null;
     64    /** Used to initiate the export, then dispose of the dialog. */
     65    private JButton export_button = null;
     66    /** The metadata sets available for export. */
     67    private JComboBox sets = null;
     68    /** The destination file name as a string. */
     69    private JTextField file_name = null;
     70    /** Ths button that, if selected, signifies you wish to export the metadata set with all values. */
     71    private JToggleButton all_values = null;
     72    /** The button that, if selected, signifies you wish to export the metadata set without any values (ie no mdv files). */
     73    private JToggleButton no_values = null;
     74    /** The button that, if selected, signifies you wish to export the metadata set with only those values that are subject nodes in the hierarchy. */
     75    private JToggleButton structure_only = null;
     76    /** A reference to the metadata set manager. */
     77    private MetadataSetManager manager;
     78    /** The default size for this dialog window. */
     79    final static public Dimension EXPORT_SIZE = new Dimension(500,220);
     80    /** The default size for this dialog window. */
     81    final static public Dimension IMPORT_SIZE = new Dimension(500,120);
     82    /** The default export action (there are several depending on how much information you wish to export). */
     83    final static public int EXPORT = 0;
     84    /** Constructor.
     85     * @param manager A reference to the <strong>MetadataSetManager</strong>.
     86     * @see org.greenstone.gatherer.Configuration
     87     * @see org.greenstone.gatherer.util.Utility
     88     */
     89    public ExportMDSPrompt(MetadataSetManager manager, boolean export) {
     90    super();
     91    this.export = export;
     92    // Creation
     93    setModal(true);
     94    if(export) {
     95        setSize(EXPORT_SIZE);
     96        setTitle(get("Export_Title"));
     97    }
     98    else {
     99        setSize(IMPORT_SIZE);
     100        setTitle(get("Import_Title"));
     101    }
     102    JPanel content_pane = (JPanel) getContentPane();
     103    JPanel control_pane = new JPanel();
     104    JLabel set_label = new JLabel(get("Export_Set"));
     105    sets = new JComboBox(manager.getSets());
     106    JLabel condition_label = new JLabel(get("Export_Conditions"));
     107    JPanel condition_pane = new JPanel();
     108    ButtonGroup condition_group = new ButtonGroup();
     109    all_values = new JToggleButton(get("Export_All_Values"));
     110    condition_group.add(all_values);
     111    no_values = new JToggleButton(get("Export_No_Values"));
     112    condition_group.add(no_values);
     113    structure_only = new JToggleButton(get("Export_Subjects_Only"));
     114    condition_group.add(structure_only);
     115    all_values.setSelected(true);
     116    JLabel file_label = new JLabel(get("Export_File"));
     117    JPanel file_pane = new JPanel();
     118    file_name = new JTextField(Utility.METADATA_DIR);
     119    browse_button = new JButton(get("General.Browse"));
     120    JPanel button_pane = new JPanel();
     121    if(export) {
     122        export_button = new JButton(get("File_Export"));
     123        export_button.setEnabled(false);
     124    }
     125    else {
     126        export_button = new JButton(get("File_Import"));
     127        export_button.setEnabled(true);
     128    }
     129    cancel_button = new JButton(get("General.Cancel"));
     130    // Listeners
     131    browse_button.addActionListener(this);
     132    cancel_button.addActionListener(this);
     133    export_button.addActionListener(this);
     134    file_name.addKeyListener(this);
     135    // Layout
     136    condition_pane.setLayout(new GridLayout(1,3));
     137    condition_pane.add(all_values);
     138    condition_pane.add(structure_only);
     139    condition_pane.add(no_values);
     140
     141    file_pane.setLayout(new BorderLayout());
     142    file_pane.add(file_name, BorderLayout.CENTER);
     143    file_pane.add(browse_button, BorderLayout.EAST);
     144
     145    if(export) {
     146        control_pane.setLayout(new GridLayout(6, 1));
     147        control_pane.add(set_label);
     148        control_pane.add(sets);
     149        control_pane.add(condition_label);
     150        control_pane.add(condition_pane);
     151        control_pane.add(file_label);
     152        control_pane.add(file_pane);
     153    }
     154    else {
     155        control_pane.setLayout(new GridLayout(2,1));
     156        control_pane.add(condition_label);
     157        control_pane.add(condition_pane);
     158    }
    159159         
    160           button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
    161           button_pane.setLayout(new GridLayout(1,2));
    162           button_pane.add(export_button);
    163           button_pane.add(cancel_button);
    164 
    165           content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    166           content_pane.setLayout(new BorderLayout());
    167           content_pane.add(control_pane, BorderLayout.CENTER);
    168           content_pane.add(button_pane, BorderLayout.SOUTH);
    169           // Display
    170           Dimension screen_size = Gatherer.config.screen_size;
    171           setLocation((screen_size.width - getSize().width) / 2, (screen_size.height - getSize().height) / 2);
    172     }
    173     /** Whenever one of the buttons in the dialog is actioned this method is called to trigger the appropriate effects.
     160    button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
     161    button_pane.setLayout(new GridLayout(1,2));
     162    button_pane.add(export_button);
     163    button_pane.add(cancel_button);
     164
     165    content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     166    content_pane.setLayout(new BorderLayout());
     167    content_pane.add(control_pane, BorderLayout.CENTER);
     168    content_pane.add(button_pane, BorderLayout.SOUTH);
     169    // Display
     170    Dimension screen_size = Gatherer.config.screen_size;
     171    setLocation((screen_size.width - getSize().width) / 2, (screen_size.height - getSize().height) / 2);
     172    }
     173    /** Whenever one of the buttons in the dialog is actioned this method is called to trigger the appropriate effects.
    174174      * @param event An <strong>ActionEvent</strong> containing information about the action performed.
    175175      * @see org.greenstone.gatherer.Gatherer
     
    177177      * @see org.greenstone.gatherer.util.Utility
    178178      */
    179     public void actionPerformed(ActionEvent event) {
    180           if(event.getSource() == export_button) {
    181                 action = EXPORT;
    182                 dispose();
    183           }
    184           else if(event.getSource() == cancel_button) {
    185                 action = -1;
    186                 dispose();
    187           }
    188           else {
    189                 JFileChooser chooser = new JFileChooser(new File(Utility.METADATA_DIR));
    190                 javax.swing.filechooser.FileFilter filter = new MDSFileFilter();
    191                 chooser.setApproveButtonText(get("General.OK"));
    192                 chooser.setFileFilter(filter);
    193                 int returnVal = chooser.showSaveDialog(Gatherer.g_man);
    194                 if(returnVal == JFileChooser.APPROVE_OPTION) {
    195                      file = chooser.getSelectedFile();
    196                      if(file.getName().indexOf(".") == -1) {
    197                           file = new File(file.getName() + ".mds");
    198                      }
    199                      file_name.setText(file.getAbsolutePath());
    200                      export_button.setEnabled(true);
    201                 }
    202           }
    203     }
    204     /** Show the prompt and get the user input.
     179    public void actionPerformed(ActionEvent event) {
     180    if(event.getSource() == export_button) {
     181        action = EXPORT;
     182        dispose();
     183    }
     184    else if(event.getSource() == cancel_button) {
     185        action = -1;
     186        dispose();
     187    }
     188    else {
     189        JFileChooser chooser = new JFileChooser(new File(Utility.METADATA_DIR));
     190        javax.swing.filechooser.FileFilter filter = new MDSFileFilter();
     191        chooser.setApproveButtonText(get("General.OK"));
     192        chooser.setFileFilter(filter);
     193        int returnVal = chooser.showSaveDialog(Gatherer.g_man);
     194        if(returnVal == JFileChooser.APPROVE_OPTION) {
     195        file = chooser.getSelectedFile();
     196        if(file.getName().indexOf(".") == -1) {
     197            file = new File(file.getName() + ".mds");
     198        }
     199        file_name.setText(file.getAbsolutePath());
     200        export_button.setEnabled(true);
     201        }
     202    }
     203    }
     204    /** Show the prompt and get the user input.
    205205      * @return An <i>int</i> specifying what action the user has choosen.
    206206      */
    207     public int display() {
    208           show();
    209           return action;
    210     }
    211     /** Get the current value of condition.
     207    public int display() {
     208    show();
     209    return action;
     210    }
     211    /** Get the current value of condition.
    212212      * @return The value as an <i>int</i>.
    213213      * @see org.greenstone.gatherer.msm.MetadataSet
    214214      */
    215     public int getSelectedCondition() {
    216           if(all_values.isSelected()) {
    217                 return MetadataSet.ALL_VALUES;
    218           }
    219           else if(no_values.isSelected()) {
    220                 return MetadataSet.NO_VALUES;
    221           }
    222           else {
    223                 return MetadataSet.SUBJECTS_ONLY;
    224           }
    225     }
    226     /** Get the current value of file.
     215    public int getSelectedCondition() {
     216    if(all_values.isSelected()) {
     217        return MetadataSet.ALL_VALUES;
     218    }
     219    else if(no_values.isSelected()) {
     220        return MetadataSet.NO_VALUES;
     221    }
     222    else {
     223        return MetadataSet.SUBJECTS_ONLY;
     224    }
     225    }
     226    /** Get the current value of file.
    227227      * @return The value as a <strong>File</strong>.
    228228      */
    229     public File getSelectedFile() {
    230           return file;
    231     }
    232     /** Get the current value of set.
     229    public File getSelectedFile() {
     230    return file;
     231    }
     232    /** Get the current value of set.
    233233      * @return The value as a <strong>MetadataSet</strong>.
    234234      */
    235     public MetadataSet getSelectedSet() {
    236           return (MetadataSet) sets.getSelectedItem();
    237     }
    238     /** Any implementation of KeyListener must include this method so that we can be informed when a key has been pressed. In this case we ignore it.
     235    public MetadataSet getSelectedSet() {
     236    return (MetadataSet) sets.getSelectedItem();
     237    }
     238    /** Any implementation of KeyListener must include this method so that we can be informed when a key has been pressed. In this case we ignore it.
    239239      * @param event A <strong>KeyEvent</strong> containing information about the key pressed.
    240240      */
    241     public void keyPressed(KeyEvent event) {
    242     }
    243     /** Any implementation of KeyListener must include this method so that we can be informed once a key has been released. This is the earliest the VK code becomes stable and usable, so we will check if the file named in file_name can be written to and if so enable the export button.
     241    public void keyPressed(KeyEvent event) {
     242    }
     243    /** Any implementation of KeyListener must include this method so that we can be informed once a key has been released. This is the earliest the VK code becomes stable and usable, so we will check if the file named in file_name can be written to and if so enable the export button.
    244244      * @param event A <strong>KeyEvent</strong> containing information about the key typed.
    245245      */
    246     public void keyReleased(KeyEvent event) {
    247           String pos_file = file_name.getText();
    248           if(pos_file.indexOf(".") != -1) {
    249                 file = new File(file_name.getText());
    250                 if(file.canWrite() || !file.exists()) {
    251                      export_button.setEnabled(true);
    252                 }
    253                 else if(export) {
    254                      export_button.setEnabled(false);
    255                 }
    256           }
    257     }
    258     /** Any implementation of KeyListener must include this method so that we can be informed when a key has been typed. In this case we ignore it.
     246    public void keyReleased(KeyEvent event) {
     247    String pos_file = file_name.getText();
     248    if(pos_file.indexOf(".") != -1) {
     249        file = new File(file_name.getText());
     250        if(file.canWrite() || !file.exists()) {
     251        export_button.setEnabled(true);
     252        }
     253        else if(export) {
     254        export_button.setEnabled(false);
     255        }
     256    }
     257    }
     258    /** Any implementation of KeyListener must include this method so that we can be informed when a key has been typed. In this case we ignore it.
    259259      * @param event A <strong>KeyEvent</strong> containing information about the key typed.
    260260      */
    261     public void keyTyped(KeyEvent event) {
    262     }
    263 
    264     private String get(String key) {
    265           return get(key, null);
    266     }
    267     private String get(String key, String[] args) {
    268           if(key.indexOf(".") == -1) {
    269                 key = "MSMPrompt." + key;
    270           }
    271           return Gatherer.dictionary.get(key, args);
    272     }
     261    public void keyTyped(KeyEvent event) {
     262    }
     263
     264    private String get(String key) {
     265    return get(key, null);
     266    }
     267    private String get(String key, String[] args) {
     268    if(key.indexOf(".") == -1) {
     269        key = "MSMPrompt." + key;
     270    }
     271    return Gatherer.dictionary.get(key, args);
     272    }
    273273}
  • trunk/gli/src/org/greenstone/gatherer/msm/GDMDocument.java

    r4358 r4365  
    4141 */
    4242public class GDMDocument {
    43     /** Record if the document this object is based on is up to date. */
    44     private boolean up_to_date = true;
    45     /** The document this class sources its data from. */
    46     private Document base_document;
    47     static final private String ACCUMULATE = "accumulate";
    48     /** The pattern to match when searching for directory level assignments. */
    49     static final private String DIRECTORY_FILENAME = ".*";
    50     static final private String DESCRIPTION_ELEMENT = "Description";
    51     static final private String FILENAME_ELEMENT = "FileName";
    52     static final private String FILESET_ELEMENT = "FileSet";
    53     static final private String HVALUE_ATTRIBUTE = "hvalue";
    54     static final private String METADATA_ELEMENT = "Metadata";
    55     static final private String MODE_ATTRIBUTE = "mode";
    56     static final private String NAME_ATTRIBUTE = "name";
    57     static final private String OVERWRITE = "overwrite";
    58     /** Constructor which creates a brand new metadata.xml document. */
    59     public GDMDocument() {
    60           // Create new document. We do this by loading a copy of the template. */
    61           this.base_document = Utility.parse(Utility.GREENSTONEDIRECTORYMETADATA_TEMPLATE, true);
    62     }
    63     /** Constructor which parses an existing metadata.xml document. */
    64     public GDMDocument(File file) {
    65           try {
    66                 this.base_document = Utility.parse(file.getAbsolutePath(), false);
    67           }
    68           catch (Exception error) {
     43    /** Record if the document this object is based on is up to date. */
     44    private boolean up_to_date = true;
     45    /** The document this class sources its data from. */
     46    private Document base_document;
     47    static final private String ACCUMULATE = "accumulate";
     48    /** The pattern to match when searching for directory level assignments. */
     49    static final private String DIRECTORY_FILENAME = ".*";
     50    static final private String DESCRIPTION_ELEMENT = "Description";
     51    static final private String FILENAME_ELEMENT = "FileName";
     52    static final private String FILESET_ELEMENT = "FileSet";
     53    static final private String HVALUE_ATTRIBUTE = "hvalue";
     54    static final private String METADATA_ELEMENT = "Metadata";
     55    static final private String MODE_ATTRIBUTE = "mode";
     56    static final private String NAME_ATTRIBUTE = "name";
     57    static final private String OVERWRITE = "overwrite";
     58    /** Constructor which creates a brand new metadata.xml document. */
     59    public GDMDocument() {
     60    // Create new document. We do this by loading a copy of the template. */
     61    this.base_document = Utility.parse(Utility.GREENSTONEDIRECTORYMETADATA_TEMPLATE, true);
     62    }
     63    /** Constructor which parses an existing metadata.xml document. */
     64    public GDMDocument(File file) {
     65    try {
     66        this.base_document = Utility.parse(file.getAbsolutePath(), false);
     67    }
     68    catch (Exception error) {
    6969                // Poorly formed, or completely invalid metadata.xml file!
    70           }
    71     }
    72     /** Constructor which wraps around an existing metadata.xml document. */
    73     public GDMDocument(Document base_document) {
    74           this.base_document = base_document;
    75     }
    76     /** Add this metadata to the named file. There is one tricky thing to consider. Whenever a metadata entry is added it is taken to be accumulating except if it is the first added, in which case it overwrites! */
    77     public void addMetadata(String filename, Metadata metadata) {
    78           System.err.println("Add '" + metadata + "' to " + (filename != null ? filename : "directory."));
    79           try {
     70    }
     71    }
     72    /** Constructor which wraps around an existing metadata.xml document. */
     73    public GDMDocument(Document base_document) {
     74    this.base_document = base_document;
     75    }
     76    /** Add this metadata to the named file. There is one tricky thing to consider. Whenever a metadata entry is added it is taken to be accumulating except if it is the first added, in which case it overwrites! */
     77    public void addMetadata(String filename, Metadata metadata) {
     78    System.err.println("Add '" + metadata + "' to " + (filename != null ? filename : "directory."));
     79    try {
    8080                // Retrieve the document element.
    81                 Element directorymetadata_element = base_document.getDocumentElement();
     81        Element directorymetadata_element = base_document.getDocumentElement();
    8282                // Iterate through the filesets looking for one that matches the given filename.
    83                 Element fileset_element = null;
    84                 NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT);
    85                 for(int i = 0; i < fileset_elements.getLength(); i++) {
    86                      fileset_element = (Element) fileset_elements.item(i);
    87                      NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT);
    88                      boolean found = false;
    89                      for(int j = 0; !found && j < filename_elements.getLength(); j++) {
    90                           Element filename_element = (Element) filename_elements.item(j);
    91                           String filename_pattern = MSMUtils.getValue(filename_element);
    92                           // Have we found a match. If so break out of for loop.
    93                           if(filename != null && filename.matches(filename_pattern) && !filename_pattern.equals(DIRECTORY_FILENAME)) {
    94                                 System.err.println("Adding to existing file fileset!");
    95                                 found = true;
    96                           }
    97                           else if(filename == null && filename_pattern.equals(DIRECTORY_FILENAME)) {
    98                                 System.err.println("Adding to existing folder fileset!");
    99                                 ///ystem.err.println("filename_pattern = '" + filename_pattern + "'");
    100                                 found = true;
    101                           }
    102                           // No match. On to the next one.
    103                           else {
    104                                 fileset_element = null;
    105                           }
    106                           filename_pattern = null;
    107                           filename_element = null;
    108                      }
    109                 }
    110                 fileset_elements = null;
     83        Element fileset_element = null;
     84        NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT);
     85        for(int i = 0; i < fileset_elements.getLength(); i++) {
     86        fileset_element = (Element) fileset_elements.item(i);
     87        NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT);
     88        boolean found = false;
     89        for(int j = 0; !found && j < filename_elements.getLength(); j++) {
     90            Element filename_element = (Element) filename_elements.item(j);
     91            String filename_pattern = MSMUtils.getValue(filename_element);
     92            // Have we found a match. If so break out of for loop.
     93            if(filename != null && filename.matches(filename_pattern) && !filename_pattern.equals(DIRECTORY_FILENAME)) {
     94            System.err.println("Adding to existing file fileset!");
     95            found = true;
     96            }
     97            else if(filename == null && filename_pattern.equals(DIRECTORY_FILENAME)) {
     98            System.err.println("Adding to existing folder fileset!");
     99            ///ystem.err.println("filename_pattern = '" + filename_pattern + "'");
     100            found = true;
     101            }
     102            // No match. On to the next one.
     103            else {
     104            fileset_element = null;
     105            }
     106            filename_pattern = null;
     107            filename_element = null;
     108        }
     109        }
     110        fileset_elements = null;
    111111                // If we still haven't found an existing fileset, then its time to create one.
    112                 if(fileset_element == null) {
    113                      System.err.println("Creating a new fileset.");
    114                      fileset_element = base_document.createElement(FILESET_ELEMENT);
    115                      Element filename_element = base_document.createElement(FILENAME_ELEMENT);
    116                      Element description_element = base_document.createElement(DESCRIPTION_ELEMENT);
    117                      fileset_element.appendChild(filename_element);
    118                      fileset_element.appendChild(description_element);
    119                      Text filename_text = null;
    120                      // If the filename is null then we add a directory metadata set as directorymetadata_element's first child
    121                      if(filename == null) {
    122                           filename_text = base_document.createTextNode(DIRECTORY_FILENAME);
    123                           if(directorymetadata_element.hasChildNodes()) {
    124                                 directorymetadata_element.insertBefore(fileset_element, directorymetadata_element.getFirstChild());
    125                           }
    126                           else {
    127                                 directorymetadata_element.appendChild(fileset_element);
    128                           }
    129                      }
    130                      // Otherwise we just append the new fileset to directorymetadata_element's children.
    131                      else {
    132                           filename_text = base_document.createTextNode(filename);
    133                           directorymetadata_element.appendChild(fileset_element);
    134                      }
    135                      filename_element.appendChild(filename_text);
    136                      filename_text = null;
    137                      description_element = null;
    138                      filename_element = null;
    139                 }
     112        if(fileset_element == null) {
     113        System.err.println("Creating a new fileset.");
     114        fileset_element = base_document.createElement(FILESET_ELEMENT);
     115        Element filename_element = base_document.createElement(FILENAME_ELEMENT);
     116        Element description_element = base_document.createElement(DESCRIPTION_ELEMENT);
     117        fileset_element.appendChild(filename_element);
     118        fileset_element.appendChild(description_element);
     119        Text filename_text = null;
     120        // If the filename is null then we add a directory metadata set as directorymetadata_element's first child
     121        if(filename == null) {
     122            filename_text = base_document.createTextNode(DIRECTORY_FILENAME);
     123            if(directorymetadata_element.hasChildNodes()) {
     124            directorymetadata_element.insertBefore(fileset_element, directorymetadata_element.getFirstChild());
     125            }
     126            else {
     127            directorymetadata_element.appendChild(fileset_element);
     128            }
     129        }
     130        // Otherwise we just append the new fileset to directorymetadata_element's children.
     131        else {
     132            filename_text = base_document.createTextNode(filename);
     133            directorymetadata_element.appendChild(fileset_element);
     134        }
     135        filename_element.appendChild(filename_text);
     136        filename_text = null;
     137        description_element = null;
     138        filename_element = null;
     139        }
    140140                // Now, finally, we can add the metadata.
    141                 Element metadata_element = base_document.createElement(METADATA_ELEMENT);
    142                 metadata_element.setAttribute(NAME_ATTRIBUTE, metadata.getElement().getName());
     141        Element metadata_element = base_document.createElement(METADATA_ELEMENT);
     142        metadata_element.setAttribute(NAME_ATTRIBUTE, metadata.getElement().getName());
    143143
    144144                // To determine if this metadata entry should overwrite or accumulate we check if there are other entries with the same element in this fileset.
    145                 boolean will_accumulate = false;
    146                 NodeList sibling_description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT);
    147                 for(int k = 0; !will_accumulate && k < sibling_description_elements.getLength(); k++) {
    148                      Element sibling_description_element = (Element) sibling_description_elements.item(k);
    149                      NodeList sibling_metadata_elements = sibling_description_element.getElementsByTagName(METADATA_ELEMENT);
    150                      for(int l = 0; !will_accumulate && l < sibling_metadata_elements.getLength(); l++) {
    151                           Element sibling_metadata_element = (Element) sibling_metadata_elements.item(l);
    152                           will_accumulate = sibling_metadata_element.getAttribute(NAME_ATTRIBUTE).equals(metadata_element.getAttribute(NAME_ATTRIBUTE));
    153                           sibling_metadata_element = null;
    154                      }
    155                      sibling_metadata_elements = null;
    156                      sibling_description_element = null;
    157                 }
    158                 sibling_description_elements = null;
    159                 if(will_accumulate) { //mode.equals(ACCUMULATE)) {
    160                      metadata_element.setAttribute(MODE_ATTRIBUTE, ACCUMULATE);
    161                 }
     145        boolean will_accumulate = false;
     146        NodeList sibling_description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT);
     147        for(int k = 0; !will_accumulate && k < sibling_description_elements.getLength(); k++) {
     148        Element sibling_description_element = (Element) sibling_description_elements.item(k);
     149        NodeList sibling_metadata_elements = sibling_description_element.getElementsByTagName(METADATA_ELEMENT);
     150        for(int l = 0; !will_accumulate && l < sibling_metadata_elements.getLength(); l++) {
     151            Element sibling_metadata_element = (Element) sibling_metadata_elements.item(l);
     152            will_accumulate = sibling_metadata_element.getAttribute(NAME_ATTRIBUTE).equals(metadata_element.getAttribute(NAME_ATTRIBUTE));
     153            sibling_metadata_element = null;
     154        }
     155        sibling_metadata_elements = null;
     156        sibling_description_element = null;
     157        }
     158        sibling_description_elements = null;
     159        if(will_accumulate) { //mode.equals(ACCUMULATE)) {
     160        metadata_element.setAttribute(MODE_ATTRIBUTE, ACCUMULATE);
     161        }
    162162                // As we can't possibly store all the metadata in memory, nor can we ensure that the indexes written to file remain the same until the new time we look at this file, and to avoid having to open a rewrite every collection document whenever any value tree changes, I'm adding a new attribute called hvalue which indicates the hierarchy value path as a '\' separated string.
    163                 GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(metadata.getElement());
    164                 if(model != null && model.isHierarchy()) {
    165                      metadata_element.setAttribute(HVALUE_ATTRIBUTE, metadata.getValueNode().getFullPath());
    166                 }
    167                 metadata_element.appendChild(base_document.createTextNode(metadata.getAbsoluteValue()));
     163        GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(metadata.getElement());
     164        if(model != null && model.isHierarchy()) {
     165        metadata_element.setAttribute(HVALUE_ATTRIBUTE, metadata.getValueNode().getFullPath());
     166        }
     167        metadata_element.appendChild(base_document.createTextNode(metadata.getAbsoluteValue()));
    168168                // Retrieve the first description element for this fileset (there should only be one, but I'll play it safe).
    169                 NodeList description_elements = fileset_element.getElementsByTagName("Description");
    170                 Element description_element = (Element) description_elements.item(0);
    171                 description_element.appendChild(metadata_element);
    172                 description_element = null;
     169        NodeList description_elements = fileset_element.getElementsByTagName("Description");
     170        Element description_element = (Element) description_elements.item(0);
     171        description_element.appendChild(metadata_element);
     172        description_element = null;
     173        metadata_element = null;
     174                //mode = null;
     175        fileset_element = null;
     176        directorymetadata_element = null;
     177        up_to_date = false;
     178    }
     179    catch (Exception error) {
     180        Gatherer.printStackTrace(error);
     181    }
     182    }
     183
     184    /** Retrieve the document this class is wrapping. */
     185    public Document getDocument() {
     186    return base_document;
     187    }
     188    /** Get all of the metadata, including directory level, associated with this file. */
     189    public ArrayList getMetadata(String filename, boolean remove, ArrayList metadatum_so_far, File file) {
     190    return getMetadata(filename, remove, metadatum_so_far, file, false);
     191    }
     192    /** Retrieve the metadata associated with the given filename. Keep track of what metadata should be overwritten and what should be accumulated. Also make note of the source file, and remove the metadata if required. Finally if purge is set retrieve every piece of metadata in this file. */
     193    public ArrayList getMetadata(String filename, boolean remove, ArrayList metadatum_so_far, File file, boolean purge) {
     194    ///ystem.err.println("Get metadata for " + filename);
     195    ArrayList metadatum = null;
     196    if(metadatum_so_far == null) {
     197        metadatum = new ArrayList();
     198    }
     199    else {
     200        metadatum = metadatum_so_far;
     201    }
     202    try {
     203                // Retrieve the document element.
     204        Element directorymetadata_element = base_document.getDocumentElement();
     205                // Iterate through the filesets, checking the FileName child element against    the target file's name using regular expression matching.
     206        NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT);
     207        for(int i = 0; i < fileset_elements.getLength(); i++) {
     208        Element fileset_element = (Element) fileset_elements.item(i);
     209        NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT);
     210        for(int j = 0; j < filename_elements.getLength(); j++) {
     211            Element filename_element = (Element) filename_elements.item(j);
     212            String filename_text = MSMUtils.getValue(filename_element);
     213            if((filename != null && filename.matches(filename_text)) || filename_text.equals(DIRECTORY_FILENAME) || purge) {
     214            // If they match add all of the metadata found in the Description child element, remembering to abide by desired mode (accumulate vs. overwrite).
     215            NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT);
     216            for(int k = 0; k < description_elements.getLength(); k++) {
     217                Element description_element = (Element) description_elements.item(k);
     218                NodeList metadata_elements = description_element.getElementsByTagName(METADATA_ELEMENT);
     219                for(int l = 0; l < metadata_elements.getLength(); l++) {
     220                Element metadata_element = (Element) metadata_elements.item(l);
     221                String raw_element = metadata_element.getAttribute(NAME_ATTRIBUTE);
     222                //String language = metadata_element.getAttribute("language");
     223                String mode = metadata_element.getAttribute(MODE_ATTRIBUTE);
     224                String raw_value = metadata_element.getAttribute(HVALUE_ATTRIBUTE);
     225                if(raw_value == null || raw_value.length() == 0) {
     226                    raw_value = MSMUtils.getValue(metadata_element);
     227                }
     228                // Using the element string and value, retrieve a matching Metadata object from the cache
     229                Metadata metadata = null;
     230                // If this element has hierarchy values then we must ensure the raw value is a full path, not an index.
     231                if(GDMManager.metadata_cache.contains(raw_element, raw_value)) {
     232                    ///ystem.err.println("HIT! Retrieve metadata from cache: " + raw_element + " -> " + raw_value + "\n");
     233                    metadata = (Metadata) GDMManager.metadata_cache.get(raw_element, raw_value);
     234                }
     235                else {
     236                    ElementWrapper element = Gatherer.c_man.getCollection().msm.getElement(raw_element);
     237                    GValueNode value = Metadata.getDefaultValueNode(element, raw_value);
     238                    ///ystem.err.println("Miss. Create new metadata: " + raw_element + " -> " + raw_value + "\n");
     239                    metadata = new Metadata(element, value);
     240                    GDMManager.metadata_cache.put(raw_element, raw_value, metadata);
     241                    ///ystem.err.println("Added metadata to cache: " + raw_element + " -> " + raw_value + "\n");
     242                    value = null;
     243                    element = null;
     244                }
     245                // We determine whether this metadata is file or folder level
     246                if(filename != null) {
     247                    ///ystem.err.println("Filename      = " + filename);
     248                    ///ystem.err.println("filename_text = " + filename_text);
     249                    // If can only be file level if there is no folder path details in filename and if the filename matched the filename text node (it may have matched .* instead)!
     250                    if(filename.indexOf(File.separator) == -1 && filename.equals(filename_text)) {
     251                    metadata.setFileLevel(true);
     252                    ///ystem.err.println("File level!!!");
     253                    }
     254                    else {
     255                    metadata.setFileLevel(false);
     256                    ///ystem.err.println("Inherited!!!");
     257                    }
     258                }
     259                else {
     260                    ///ystem.err.println("Filename is null therefore this is file level metadata.");
     261                    metadata.setFileLevel(true);
     262                }
     263                metadata.setFile(file);
     264
     265                // If mode is overwrite, then remove any previous values for this metadata element.
     266                if(mode.equals("accumulate")) {
     267                    metadata.setAccumulate(true);
     268                }
     269                else {
     270                    metadata.setAccumulate(false);
     271                    ///ystem.err.println("Metadata overwrites: " + metadata);
     272                    for(int m = metadatum.size() - 1; m >= 0; m--) {
     273                    Metadata old_metadata = (Metadata) metadatum.get(m);
     274                    if(old_metadata.getElement().equals(metadata.getElement())) {
     275                        metadatum.remove(m);
     276                        ///ystem.err.println("Removing overridden metadata: " + old_metadata);
     277                    }
     278                    old_metadata = null;
     279                    }
     280                }
     281                mode = null;
     282
     283                // Add the completed metadata and clean up
     284                ///ystem.err.println("Adding metadata: " + metadata);
     285                metadatum.add(metadata);
     286
     287                // Having found our metadata check if the value from the xml matches the one from the gvaluenode. If not update it. This happens whenever hierarchy information is involved (indexes rapidly become obsolete).
     288                // If remove was set, remove it. We can only remove pure file level metadata, or folder level iff we were asked for folder level.
     289                if(remove && ((filename != null && filename.matches(filename_text) && !filename_text.equals(DIRECTORY_FILENAME)) || (filename == null && filename_text.equals(DIRECTORY_FILENAME)))) {
     290                    ///ystem.err.println("Removing " + metadata + " from " + file);
     291                    description_element.removeChild(metadata_element);
     292                    // Remove the description element if empty.
     293                    if(!description_element.hasChildNodes()) {
     294                    fileset_element.removeChild(description_element);
     295                    }
     296                }
     297                else {
     298                    String current_value = metadata.getAbsoluteValue();
     299                    if(!raw_value.equals(current_value)) {
     300                    // Remove old text
     301                    while(metadata_element.hasChildNodes()) {
     302                        metadata_element.removeChild(metadata_element.getFirstChild());
     303                    }
     304                    // Add new.
     305                    metadata_element.appendChild(base_document.createTextNode(current_value));
     306                    }
     307                                }
     308
     309                metadata = null;
     310                raw_value = null;
     311                raw_element = null;
    173312                metadata_element = null;
    174                 //mode = null;
    175                 fileset_element = null;
    176                 directorymetadata_element = null;
    177                 up_to_date = false;
    178           }
    179           catch (Exception error) {
    180                 Gatherer.printStackTrace(error);
    181           }
    182      }
    183 
    184      /** Retrieve the document this class is wrapping. */
    185      public Document getDocument() {
    186           return base_document;
    187      }
    188      /** Get all of the metadata, including directory level, associated with this file. */
    189      public ArrayList getMetadata(String filename, boolean remove, ArrayList metadatum_so_far, File file) {
    190           return getMetadata(filename, remove, metadatum_so_far, file, false);
    191      }
    192      /** Retrieve the metadata associated with the given filename. Keep track of what metadata should be overwritten and what should be accumulated. Also make note of the source file, and remove the metadata if required. Finally if purge is set retrieve every piece of metadata in this file. */
    193      public ArrayList getMetadata(String filename, boolean remove, ArrayList metadatum_so_far, File file, boolean purge) {
    194           ///ystem.err.println("Get metadata for " + filename);
    195           ArrayList metadatum = null;
    196           if(metadatum_so_far == null) {
    197                 metadatum = new ArrayList();
    198           }
    199           else {
    200                 metadatum = metadatum_so_far;
    201           }
    202           try {
     313                }
     314                metadata_elements = null;
     315                description_element = null;
     316            }
     317            description_elements = null;
     318            }
     319            filename_text = null;
     320            filename_element = null;
     321        }
     322        // If the file set no longer has any description entries, remove it entirely
     323        NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT);
     324        if(description_elements.getLength() == 0) {
     325            directorymetadata_element.removeChild(fileset_element);
     326        }
     327        description_elements = null;
     328        filename_elements = null;
     329        fileset_element = null;
     330        }
     331        fileset_elements = null;
     332        directorymetadata_element = null;
     333    }
     334    catch (Exception error) {
     335        Gatherer.self.printStackTrace(error);
     336    }
     337    ///ystem.err.println("Found " + metadatum.size() + " pieces of metadata.");
     338    return metadatum;
     339    }
     340
     341    /** Determine if this document has been saved recently, and thus xml file version is up to date. */
     342    public boolean isUpToDate() {
     343    return false;
     344    }
     345
     346    /** Determine is this is a valid Greenstone Directory Metadata file. It may of course just be some xml file with the name metadata.xml. */
     347    public boolean isValid() {
     348    // Just determine if the doctype is GreenstoneDirectoryMetadata and root node is called DirectoryMetadata.
     349    String doctype_name = base_document.getDoctype().getName();
     350    String root_name = base_document.getDocumentElement().getTagName();
     351    return ((doctype_name.equals("GreenstoneDirectoryMetadata") && root_name.equals("GreenstoneDirectoryMetadata")) || (doctype_name.equals("DirectoryMetadata") && root_name.equals("DirectoryMetadata")));
     352    }
     353    /** Remove the given directory level metadata from this document. All directory level metadata is available under the FileSet with filename '.*'. There is at least one nasty case to consider, where the first overwriting metadata entry, of several with the same element, is removed. In this case the next entry must become overwrite to ensure proper inheritance. */
     354    public void removeMetadata(String filename, Metadata metadata) {
     355    try {
     356        boolean found = false;
     357        boolean first_metadata_element_found = true;
     358        boolean make_next_metadata_element_overwrite = false;
    203359                // Retrieve the document element.
    204                 Element directorymetadata_element = base_document.getDocumentElement();
    205                 // Iterate through the filesets, checking the FileName child element against    the target file's name using regular expression matching.
    206                 NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT);
    207                 for(int i = 0; i < fileset_elements.getLength(); i++) {
    208                      Element fileset_element = (Element) fileset_elements.item(i);
    209                      NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT);
    210                      for(int j = 0; j < filename_elements.getLength(); j++) {
    211                           Element filename_element = (Element) filename_elements.item(j);
    212                           String filename_text = MSMUtils.getValue(filename_element);
    213                           if((filename != null && filename.matches(filename_text)) || filename_text.equals(DIRECTORY_FILENAME) || purge) {
    214                                 // If they match add all of the metadata found in the Description child element, remembering to abide by desired mode (accumulate vs. overwrite).
    215                                 NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT);
    216                                 for(int k = 0; k < description_elements.getLength(); k++) {
    217                                      Element description_element = (Element) description_elements.item(k);
    218                                      NodeList metadata_elements = description_element.getElementsByTagName(METADATA_ELEMENT);
    219                                      for(int l = 0; l < metadata_elements.getLength(); l++) {
    220                                           Element metadata_element = (Element) metadata_elements.item(l);
    221                                           String raw_element = metadata_element.getAttribute(NAME_ATTRIBUTE);
    222                                           //String language = metadata_element.getAttribute("language");
    223                                           String mode = metadata_element.getAttribute(MODE_ATTRIBUTE);
    224                                           String raw_value = metadata_element.getAttribute(HVALUE_ATTRIBUTE);
    225                                           if(raw_value == null || raw_value.length() == 0) {
    226                                                 raw_value = MSMUtils.getValue(metadata_element);
    227                                           }
    228                                           // Using the element string and value, retrieve a matching Metadata object from the cache
    229                                           Metadata metadata = null;
    230                                           // If this element has hierarchy values then we must ensure the raw value is a full path, not an index.
    231                                           if(GDMManager.metadata_cache.contains(raw_element, raw_value)) {
    232                                                 ///ystem.err.println("HIT! Retrieve metadata from cache: " + raw_element + " -> " + raw_value + "\n");
    233                                                 metadata = (Metadata) GDMManager.metadata_cache.get(raw_element, raw_value);
    234                                           }
    235                                           else {
    236                                                 ElementWrapper element = Gatherer.c_man.getCollection().msm.getElement(raw_element);
    237                                                 GValueNode value = Metadata.getDefaultValueNode(element, raw_value);
    238                                                 ///ystem.err.println("Miss. Create new metadata: " + raw_element + " -> " + raw_value + "\n");
    239                                                 metadata = new Metadata(element, value);
    240                                                 GDMManager.metadata_cache.put(raw_element, raw_value, metadata);
    241                                                 ///ystem.err.println("Added metadata to cache: " + raw_element + " -> " + raw_value + "\n");
    242                                                 value = null;
    243                                                 element = null;
    244                                           }
    245                                           // We determine whether this metadata is file or folder level
    246                                           if(filename != null) {
    247                                                 ///ystem.err.println("Filename      = " + filename);
    248                                                 ///ystem.err.println("filename_text = " + filename_text);
    249                                                 // If can only be file level if there is no folder path details in filename and if the filename matched the filename text node (it may have matched .* instead)!
    250                                                 if(filename.indexOf(File.separator) == -1 && filename.equals(filename_text)) {
    251                                                      metadata.setFileLevel(true);
    252                                                      ///ystem.err.println("File level!!!");
    253                                                 }
    254                                                 else {
    255                                                      metadata.setFileLevel(false);
    256                                                      ///ystem.err.println("Inherited!!!");
    257                                                 }
    258                                           }
    259                                           else {
    260                                                 ///ystem.err.println("Filename is null therefore this is file level metadata.");
    261                                                 metadata.setFileLevel(true);
    262                                           }
    263                                           metadata.setFile(file);
    264 
    265                                           // If mode is overwrite, then remove any previous values for this metadata element.
    266                                           if(mode.equals("accumulate")) {
    267                                                 metadata.setAccumulate(true);
    268                                           }
    269                                           else {
    270                                                 metadata.setAccumulate(false);
    271                                                 ///ystem.err.println("Metadata overwrites: " + metadata);
    272                                                 for(int m = metadatum.size() - 1; m >= 0; m--) {
    273                                                      Metadata old_metadata = (Metadata) metadatum.get(m);
    274                                                      if(old_metadata.getElement().equals(metadata.getElement())) {
    275                                                           metadatum.remove(m);
    276                                                           ///ystem.err.println("Removing overridden metadata: " + old_metadata);
    277                                                      }
    278                                                      old_metadata = null;
    279                                                 }
    280                                           }
    281                                           mode = null;
    282 
    283                                           // Add the completed metadata and clean up
    284                                           ///ystem.err.println("Adding metadata: " + metadata);
    285                                           metadatum.add(metadata);
    286 
    287                                           // Having found our metadata check if the value from the xml matches the one from the gvaluenode. If not update it. This happens whenever hierarchy information is involved (indexes rapidly become obsolete).
    288                                           // If remove was set, remove it. We can only remove pure file level metadata, or folder level iff we were asked for folder level.
    289                                           if(remove && ((filename != null && filename.matches(filename_text) && !filename_text.equals(DIRECTORY_FILENAME)) || (filename == null && filename_text.equals(DIRECTORY_FILENAME)))) {
    290                                                 ///ystem.err.println("Removing " + metadata + " from " + file);
    291                                                 description_element.removeChild(metadata_element);
    292                                                 // Remove the description element if empty.
    293                                                 if(!description_element.hasChildNodes()) {
    294                                                      fileset_element.removeChild(description_element);
    295                                                 }
    296                                           }
    297                                           else {
    298                                               String current_value = metadata.getAbsoluteValue();
    299                                               if(!raw_value.equals(current_value)) {
    300                                                     // Remove old text
    301                                                     while(metadata_element.hasChildNodes()) {
    302                                                          metadata_element.removeChild(metadata_element.getFirstChild());
    303                                                     }
    304                                                     // Add new.
    305                                                     metadata_element.appendChild(base_document.createTextNode(current_value));
    306                                               }
    307                                 }
    308 
    309                                           metadata = null;
    310                                           raw_value = null;
    311                                           raw_element = null;
    312                                           metadata_element = null;
    313                                      }
    314                                      metadata_elements = null;
    315                                      description_element = null;
    316                                 }
    317                                 description_elements = null;
    318                           }
    319                           filename_text = null;
    320                           filename_element = null;
    321                      }
    322                      // If the file set no longer has any description entries, remove it entirely
    323                      NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT);
    324                      if(description_elements.getLength() == 0) {
    325                           directorymetadata_element.removeChild(fileset_element);
    326                      }
    327                      description_elements = null;
    328                      filename_elements = null;
    329                      fileset_element = null;
    330                 }
    331                 fileset_elements = null;
    332                 directorymetadata_element = null;
    333           }
    334           catch (Exception error) {
    335                 Gatherer.self.printStackTrace(error);
    336           }
    337           ///ystem.err.println("Found " + metadatum.size() + " pieces of metadata.");
    338           return metadatum;
    339      }
    340 
    341      /** Determine if this document has been saved recently, and thus xml file version is up to date. */
    342      public boolean isUpToDate() {
    343           return false;
    344      }
    345 
    346      /** Determine is this is a valid Greenstone Directory Metadata file. It may of course just be some xml file with the name metadata.xml. */
    347      public boolean isValid() {
    348           // Just determine if the doctype is GreenstoneDirectoryMetadata and root node is called DirectoryMetadata.
    349           String doctype_name = base_document.getDoctype().getName();
    350           String root_name = base_document.getDocumentElement().getTagName();
    351           return ((doctype_name.equals("GreenstoneDirectoryMetadata") && root_name.equals("GreenstoneDirectoryMetadata")) || (doctype_name.equals("DirectoryMetadata") && root_name.equals("DirectoryMetadata")));
    352      }
    353      /** Remove the given directory level metadata from this document. All directory level metadata is available under the FileSet with filename '.*'. There is at least one nasty case to consider, where the first overwriting metadata entry, of several with the same element, is removed. In this case the next entry must become overwrite to ensure proper inheritance. */
    354      public void removeMetadata(String filename, Metadata metadata) {
    355           try {
    356                 boolean found = false;
    357                 boolean first_metadata_element_found = true;
    358                 boolean make_next_metadata_element_overwrite = false;
    359                 // Retrieve the document element.
    360                 Element directorymetadata_element = base_document.getDocumentElement();
     360        Element directorymetadata_element = base_document.getDocumentElement();
    361361                // Iterate through the filesets looking for the directory level one.
    362                 NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT);
    363                 for(int i = 0; i < fileset_elements.getLength(); i++) {
    364                      Element fileset_element = (Element) fileset_elements.item(i);
    365                      NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT);
    366                      for(int j = 0; j < filename_elements.getLength(); j++) {
    367                           Element filename_element = (Element) filename_elements.item(j);
    368                           String filename_text = MSMUtils.getValue(filename_element);
    369                           if((filename != null && filename.matches(filename_text)) || filename_text.equals(DIRECTORY_FILENAME)) {
    370                                 // Retrieve the Metadata Element for this fileset, and iterate through them looking for the one which we are to remove.
    371                                 NodeList description_elements = fileset_element.getElementsByTagName("Description");
    372                                 for(int k = 0; k < description_elements.getLength(); k++) {
    373                                     Element description_element = (Element) description_elements.item(k);
    374                                     NodeList metadata_elements = description_element.getElementsByTagName("Metadata");
    375                                     for(int l = 0; !found && !make_next_metadata_element_overwrite && l < metadata_elements.getLength(); l++) {       
    376                                           Element metadata_element = (Element) metadata_elements.item(l);
    377                                           String element = metadata_element.getAttribute("name");
    378                                           String value = MSMUtils.getValue(metadata_element);
    379                                           // See if this is the metadata we wish to remove
    380                                           if(element.equals(metadata.getElement().getName())) {
    381                                                 if(value.equals(metadata.getAbsoluteValue())) {
    382                                                   // Remove it
    383                                                      System.err.println("Remove " + element + "-" + value);
    384                                                      description_element.removeChild(metadata_element);
    385                                                      found = true;
    386                                                      // If this was the first metadata with this element found, and it was set to overwrite, then we have to ensure that the next metadata with this element found (if any) is changed to be overwrite now.
    387                                                      if(first_metadata_element_found && !metadata.accumulates()) {
    388                                                           make_next_metadata_element_overwrite = true;
    389                                                      }
    390                                                 }
    391                                                 // If this was the first metadata we've found with the element of the one to be removed set first found to false.
    392                                                 else if(first_metadata_element_found) {
    393                                                      first_metadata_element_found = false;
    394                                                 }
    395                                                 // Otherwise we should make this metadata overwrite as requested.
    396                                                 else if(make_next_metadata_element_overwrite) {
    397                                                      metadata_element.setAttribute(MODE_ATTRIBUTE, "");
    398                                                 }
    399                                           }
    400                                           value = null;
    401                                           element = null;
    402                                           metadata_element = null;
    403                                     }
    404                                     metadata_elements = null;
    405                                     description_element = null;
    406                                 }
    407                                 description_elements = null;
    408                           }
    409                           filename_text = null;
    410                           filename_element = null;
    411                      }
    412                      filename_elements = null;
    413                      fileset_element = null;
    414                 }
    415                 fileset_elements = null;
    416                 directorymetadata_element = null;
    417                 up_to_date = false;
    418           }
    419           catch (Exception error) {
    420                 Gatherer.printStackTrace(error);
    421           }
    422    
    423 
    424      /** Change the up to date flag. */
    425     public void setUpToDate(boolean up_to_date) {
    426           this.up_to_date = up_to_date;
    427     }
    428 
    429     /** Decode a string that was previously made Perl safe. */
    430     private String decode(String safe) {
    431           return safe.replaceAll("\\\\.",".");
    432     }
    433 
    434     /** Encodes unsafe filename characters (such as the . before the file extension) into Perl safe ones. */
    435     private String encode(String dangerous) {
    436           return dangerous.replaceAll("\\.", "\\\\.");
    437     }
     362        NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT);
     363        for(int i = 0; i < fileset_elements.getLength(); i++) {
     364        Element fileset_element = (Element) fileset_elements.item(i);
     365        NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT);
     366        for(int j = 0; j < filename_elements.getLength(); j++) {
     367            Element filename_element = (Element) filename_elements.item(j);
     368            String filename_text = MSMUtils.getValue(filename_element);
     369            if((filename != null && filename.matches(filename_text)) || filename_text.equals(DIRECTORY_FILENAME)) {
     370            // Retrieve the Metadata Element for this fileset, and iterate through them looking for the one which we are to remove.
     371            NodeList description_elements = fileset_element.getElementsByTagName("Description");
     372            for(int k = 0; k < description_elements.getLength(); k++) {
     373                Element description_element = (Element) description_elements.item(k);
     374                NodeList metadata_elements = description_element.getElementsByTagName("Metadata");
     375                for(int l = 0; !found && !make_next_metadata_element_overwrite && l < metadata_elements.getLength(); l++) {     
     376                Element metadata_element = (Element) metadata_elements.item(l);
     377                String element = metadata_element.getAttribute("name");
     378                String value = MSMUtils.getValue(metadata_element);
     379                // See if this is the metadata we wish to remove
     380                if(element.equals(metadata.getElement().getName())) {
     381                    if(value.equals(metadata.getAbsoluteValue())) {
     382                    // Remove it
     383                    System.err.println("Remove " + element + "-" + value);
     384                    description_element.removeChild(metadata_element);
     385                    found = true;
     386                    // If this was the first metadata with this element found, and it was set to overwrite, then we have to ensure that the next metadata with this element found (if any) is changed to be overwrite now.
     387                    if(first_metadata_element_found && !metadata.accumulates()) {
     388                        make_next_metadata_element_overwrite = true;
     389                    }
     390                    }
     391                    // If this was the first metadata we've found with the element of the one to be removed set first found to false.
     392                    else if(first_metadata_element_found) {
     393                    first_metadata_element_found = false;
     394                    }
     395                    // Otherwise we should make this metadata overwrite as requested.
     396                    else if(make_next_metadata_element_overwrite) {
     397                    metadata_element.setAttribute(MODE_ATTRIBUTE, "");
     398                    }
     399                }
     400                value = null;
     401                element = null;
     402                metadata_element = null;
     403                }
     404                metadata_elements = null;
     405                description_element = null;
     406            }
     407            description_elements = null;
     408            }
     409            filename_text = null;
     410            filename_element = null;
     411        }
     412        filename_elements = null;
     413        fileset_element = null;
     414        }
     415        fileset_elements = null;
     416        directorymetadata_element = null;
     417        up_to_date = false;
     418    }
     419    catch (Exception error) {
     420        Gatherer.printStackTrace(error);
     421    }
     422    }   
     423
     424   /** Change the up to date flag. */
     425    public void setUpToDate(boolean up_to_date) {
     426    this.up_to_date = up_to_date;
     427    }
     428
     429    /** Decode a string that was previously made Perl safe. */
     430    private String decode(String safe) {
     431    return safe.replaceAll("\\\\.",".");
     432    }
     433
     434    /** Encodes unsafe filename characters (such as the . before the file extension) into Perl safe ones. */
     435    private String encode(String dangerous) {
     436    return dangerous.replaceAll("\\.", "\\\\.");
     437    }
    438438}
    439 
  • trunk/gli/src/org/greenstone/gatherer/msm/GDMManager.java

    r4359 r4365  
    5353import org.w3c.dom.*;
    5454/** This object manages the metadata attached to file records. By storing all of the metadata in one place you garner several advantages. Firstly only one copy of each metadata object is retained, all actual entries are converted to references. Next you can immediately determine what metadata is assigned to an entire directory, thus the metadata.xml files can be built more effeciently (whereas the current 'optimal' method uses recursion through the tree contents). Finally, and perhaps most importantly, it allows for dynamic 'on demand' lookup of metadata. This is especially necessary with large collections, where the raw, unconnected metadata files could range into the tens of megabytes of memory and require hundreds of megabytes to read by in using serialization. Dynamic loading allows you to connect the metadata objects on load, reducing value node paths (possibly of hundreds of characters) down to a single reference pointer! At the very worst this object uses far less memory than the current method, and given that the current method is completely incapable of handling large collections, is necessary. The trade off of course is in time needed to load metadata.xml on demand, the worst possible case being the user selecting the root node of the collection tree of a large collection immediately after opening the collection. The subsequent attempt to build the metadata table will result in the metadata being loaded for every single file. But since this process is sequential and a small cache of metadata.xml files is implemented, and given that the table will actually be build on a separate thread, the wait should not be too arduous.<BR>
    55 As for the size of the GDMParser cache, I was at first tempted to put around five. However after analysis of cache usage, I determined that no gain occured because of caching (in fact if everythings working as it should there should only ever be one call for a certain metadata.xml).
     55    As for the size of the GDMParser cache, I was at first tempted to put around five. However after analysis of cache usage, I determined that no gain occured because of caching (in fact if everythings working as it should there should only ever be one call for a certain metadata.xml).
    5656*/
    5757public class GDMManager
    58     extends HashMap
    59     implements MSMListener {
    60     /** A list of the known metadata instances, thus we only store one of each unique metadata, and reference the rest. */
    61     static public HashMap3D metadata_cache = null;
    62     /** The root file node. */
    63     private TreeNode root = null;
    64     /** The threaded object responsible for loading all of the existing metadata.xml files prior to any save action. This is necessary so that hierarchy indexes within the metadata.xml files stay fresh. */
    65     private GDMLoader gdm_loader = null;
    66     static final private String METADATA_XML = "metadata.xml";
    67     /** Constructor. */
    68     public GDMManager() {
    69           super();
    70           this.metadata_cache = new HashMap3D(Gatherer.c_man.getCollection().msm.getSize());
    71           // Connect
    72           Gatherer.c_man.getCollection().msm.addMSMListener(this);
    73           // Now create and start the synchronous GDM loader.
    74           gdm_loader = new GDMLoader();
    75           gdm_loader.start();
    76           ///atherer.println("New GDMManager created.");
    77     }
    78 
    79     /** This may seem a little odd but this method doesn't add the given metadata directly, instead calling fireMetadataChanged in MetadataSetManager so as to recursively add metadata if necessary, and to ensure that all listeners who are interested in data change (such as the Metadata Table and Save listeners) can be up to date. */
    80     public void addMetadata(FileNode node, ArrayList metadatum) {
    81           for(int i = 0; i < metadatum.size(); i++) {
    82                 Metadata metadata = (Metadata) metadatum.get(i);
    83                 Gatherer.c_man.getCollection().msm.fireMetadataChanged(node, (Metadata)null, metadata);
    84           }
    85     }
    86     /** Destructor necessary for clean exit, subsequent to saving of metadata.xml files.
     58    extends HashMap
     59    implements MSMListener {
     60    /** A list of the known metadata instances, thus we only store one of each unique metadata, and reference the rest. */
     61    static public HashMap3D metadata_cache = null;
     62    /** The root file node. */
     63    private TreeNode root = null;
     64    /** The threaded object responsible for loading all of the existing metadata.xml files prior to any save action. This is necessary so that hierarchy indexes within the metadata.xml files stay fresh. */
     65    private GDMLoader gdm_loader = null;
     66    static final private String METADATA_XML = "metadata.xml";
     67    /** Constructor. */
     68    public GDMManager() {
     69    super();
     70    this.metadata_cache = new HashMap3D(Gatherer.c_man.getCollection().msm.getSize());
     71    // Connect
     72    Gatherer.c_man.getCollection().msm.addMSMListener(this);
     73    // Now create and start the synchronous GDM loader.
     74    gdm_loader = new GDMLoader();
     75    gdm_loader.start();
     76    ///atherer.println("New GDMManager created.");
     77    }
     78
     79    /** This may seem a little odd but this method doesn't add the given metadata directly, instead calling fireMetadataChanged in MetadataSetManager so as to recursively add metadata if necessary, and to ensure that all listeners who are interested in data change (such as the Metadata Table and Save listeners) can be up to date. */
     80    public void addMetadata(FileNode node, ArrayList metadatum) {
     81    for(int i = 0; i < metadatum.size(); i++) {
     82        Metadata metadata = (Metadata) metadatum.get(i);
     83        Gatherer.c_man.getCollection().msm.fireMetadataChanged(node, (Metadata)null, metadata);
     84    }
     85    }
     86    /** Destructor necessary for clean exit, subsequent to saving of metadata.xml files.
    8787      * @see org.greenstone.gatherer.Gatherer
    8888      * @see org.greenstone.gatherer.collection.CollectionManager
     
    9090      * @see org.greenstone.gatherer.msm.MetadataSetManager
    9191      */
    92     public void destroy() {
    93           // Destroy all the cached documents.
    94           /*
    95           Iterator iterator = keySet().iterator();
    96           while(iterator.hasNext()) {
    97                 File file = (File) iterator.next();
    98                 GDMDocument document = (GDMDocument) get(file);
    99                 document.destroy(file);
    100           }
    101           */
    102           // Deregister as listener
    103           Gatherer.c_man.getCollection().msm.removeMSMListener(this);
    104           // Deallocate all data members
    105           metadata_cache.clear();
    106           metadata_cache = null;
    107           // Finally clear self
    108           clear();
    109           // Done!
    110     }
    111     /** Method that is called whenever an element within a set is changed or modified. Ensure all cached GDMDocuments are marked as stale.
     92    public void destroy() {
     93    // Destroy all the cached documents.
     94    /*
     95      Iterator iterator = keySet().iterator();
     96      while(iterator.hasNext()) {
     97      File file = (File) iterator.next();
     98      GDMDocument document = (GDMDocument) get(file);
     99      document.destroy(file);
     100      }
     101    */
     102    // Deregister as listener
     103    Gatherer.c_man.getCollection().msm.removeMSMListener(this);
     104    // Deallocate all data members
     105    metadata_cache.clear();
     106    metadata_cache = null;
     107    // Finally clear self
     108    clear();
     109    // Done!
     110    }
     111    /** Method that is called whenever an element within a set is changed or modified. Ensure all cached GDMDocuments are marked as stale.
    112112      * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    113113      */
    114     public void elementChanged(MSMEvent event) {
    115           for(Iterator values = values().iterator(); values.hasNext(); ) {
    116                 GDMDocument document = (GDMDocument) values.next();
    117                 document.setUpToDate(false);
    118                 document = null;
    119           }
    120     }
    121     /** Retrieve the GreenstoneMetadataDocument that is associated with a certain file. If the document is in cache returns it. If the document exists but isn't in cache loads, caches, then returns it. Otherwise it creates a brand new document, caches it, then returns it.
     114    public void elementChanged(MSMEvent event) {
     115    for(Iterator values = values().iterator(); values.hasNext(); ) {
     116        GDMDocument document = (GDMDocument) values.next();
     117        document.setUpToDate(false);
     118        document = null;
     119    }
     120    }
     121    /** Retrieve the GreenstoneMetadataDocument that is associated with a certain file. If the document is in cache returns it. If the document exists but isn't in cache loads, caches, then returns it. Otherwise it creates a brand new document, caches it, then returns it.
    122122      * @see org.greenstone.gatherer.msm.GDMParser
    123123      */
    124     public GDMDocument getDocument(File file) {
    125           ///ystem.err.println("Get the GDMDocument for " + file.getAbsolutePath());
    126           GDMDocument metadata_xml = null;
    127           // Determine the name of the target files metadata.xml file.
    128           File metadata_file = null;
    129           if(file.isFile()) {
    130                 metadata_file = new File(file.getParentFile(), METADATA_XML);
    131           }
    132           else {
    133                 metadata_file = new File(file, METADATA_XML);
    134           }
    135           // Then try to retrieve it from cache. First we consider the case of a cache hit.
    136           if(containsKey(metadata_file)) {
    137                 metadata_xml = (GDMDocument) get(metadata_file);
    138           }
    139           else {
     124    public GDMDocument getDocument(File file) {
     125    ///ystem.err.println("Get the GDMDocument for " + file.getAbsolutePath());
     126    GDMDocument metadata_xml = null;
     127    // Determine the name of the target files metadata.xml file.
     128    File metadata_file = null;
     129    if(file.isFile()) {
     130        metadata_file = new File(file.getParentFile(), METADATA_XML);
     131    }
     132    else {
     133        metadata_file = new File(file, METADATA_XML);
     134    }
     135    // Then try to retrieve it from cache. First we consider the case of a cache hit.
     136    if(containsKey(metadata_file)) {
     137        metadata_xml = (GDMDocument) get(metadata_file);
     138    }
     139    else {
    140140                // Now the two potential cache misses. The first requires us to load an existing metadata.xml
    141                 if(metadata_file.exists()) {
    142                      metadata_xml = new GDMDocument(metadata_file);
    143                 }
     141        if(metadata_file.exists()) {
     142        metadata_xml = new GDMDocument(metadata_file);
     143        }
    144144                // The final case is where no current metadata.xml exists. Create a new one just by creating a new GDMDocument.
    145                 else {
    146                      metadata_xml = new GDMDocument();
    147                 }
    148                 put(metadata_file, metadata_xml);
     145        else {
     146        metadata_xml = new GDMDocument();
     147        }
     148        put(metadata_file, metadata_xml);
    149149                //gatherer.debug(null, "[0ms]\tCached " + metadata_file);
    150           }
    151           return metadata_xml;
    152     }
    153 
    154     /** Recover the metadata associated with a particular file. Note that this call is synchronized, so that all of the data holders don't need to be. */
    155     public synchronized ArrayList getMetadata(File file) {
    156           return getMetadata(file, false);
    157     }
    158 
    159     public synchronized ArrayList getMetadata(File file, ElementWrapper element) {
    160           ArrayList metadata = getMetadata(file, false);
    161           ArrayList values = new ArrayList();
    162           for(int i = 0; i < metadata.size(); i++) {
    163                 Metadata data = (Metadata) metadata.get(i);
    164                 if(element.equals(data.getElement())) {
    165                      values.add(data.getValue());
    166                 }
    167           }
    168           if(values.size() > 0) {
    169                 Collections.sort(values);
    170           }
    171           return values;
    172     }
    173 
    174     private ArrayList getMetadata(File file, boolean remove) {
    175           ArrayList metadata = null;
    176           String filename = null;
    177           if(file.isFile()) {
    178                 filename = file.getName();
    179                 file = file.getParentFile();
    180           }
    181           GDMDocument document = getDocument(file);
    182           if(document != null) {
    183                 metadata = document.getMetadata(filename, remove, metadata, file);
    184                 document = null;
    185           }
    186           return metadata;
    187     }
    188 
    189     public synchronized ArrayList getAllMetadata(File file) { // boolean remove) {
    190           ///ystem.err.println("getMetadata(" + file.getAbsolutePath() + ")");
    191           ArrayList metadata = null;
    192           // Build up a list of all the metadata xml files we have to check for metadata.
    193           ArrayList search_files = new ArrayList();
    194           String filename = null;
    195           File start_file = file;
    196           if(file.isFile()) {
    197                 filename = file.getName();
    198                 start_file = file.getParentFile();
    199           }
    200           File collection_dir = new File(Gatherer.c_man.getCollectionDirectory());
    201           ///ystem.err.println("Collection directory = " + collection_dir.getAbsolutePath());
    202           ///ystem.err.println("Start directory = " + start_file.getAbsolutePath());
    203           while(!start_file.equals(collection_dir)) {
     150    }
     151    return metadata_xml;
     152    }
     153
     154    /** Recover the metadata associated with a particular file. Note that this call is synchronized, so that all of the data holders don't need to be. */
     155    public synchronized ArrayList getMetadata(File file) {
     156    return getMetadata(file, false);
     157    }
     158
     159    public synchronized ArrayList getMetadata(File file, ElementWrapper element) {
     160    ArrayList metadata = getMetadata(file, false);
     161    ArrayList values = new ArrayList();
     162    for(int i = 0; i < metadata.size(); i++) {
     163        Metadata data = (Metadata) metadata.get(i);
     164        if(element.equals(data.getElement())) {
     165        values.add(data.getValue());
     166        }
     167    }
     168    if(values.size() > 0) {
     169        Collections.sort(values);
     170    }
     171    return values;
     172    }
     173
     174    private ArrayList getMetadata(File file, boolean remove) {
     175    ArrayList metadata = null;
     176    String filename = null;
     177    if(file.isFile()) {
     178        filename = file.getName();
     179        file = file.getParentFile();
     180    }
     181    GDMDocument document = getDocument(file);
     182    if(document != null) {
     183        metadata = document.getMetadata(filename, remove, metadata, file);
     184        document = null;
     185    }
     186    return metadata;
     187    }
     188
     189    public synchronized ArrayList getAllMetadata(File file) { // boolean remove) {
     190    ///ystem.err.println("getMetadata(" + file.getAbsolutePath() + ")");
     191    ArrayList metadata = null;
     192    // Build up a list of all the metadata xml files we have to check for metadata.
     193    ArrayList search_files = new ArrayList();
     194    String filename = null;
     195    File start_file = file;
     196    if(file.isFile()) {
     197        filename = file.getName();
     198        start_file = file.getParentFile();
     199    }
     200    File collection_dir = new File(Gatherer.c_man.getCollectionDirectory());
     201    ///ystem.err.println("Collection directory = " + collection_dir.getAbsolutePath());
     202    ///ystem.err.println("Start directory = " + start_file.getAbsolutePath());
     203    while(!start_file.equals(collection_dir)) {
    204204                ///ystem.err.println("Blip!");
    205                 search_files.add(0, new MetadataXMLFileSearch(start_file, filename));
    206                 if(filename != null) {
    207                      filename = start_file.getName() + "/" + filename;
    208                 }
    209                 else {
    210                      filename = start_file.getName() + "/";
    211                 }
    212                 start_file = start_file.getParentFile();
     205        search_files.add(0, new MetadataXMLFileSearch(start_file, filename));
     206        if(filename != null) {
     207        filename = start_file.getName() + "/" + filename;
     208        }
     209        else {
     210        filename = start_file.getName() + "/";
     211        }
     212        start_file = start_file.getParentFile();
    213213                ///ystem.err.println("Start directory = " + start_file.getAbsolutePath());
    214           }
    215           // Now search each of these metadata xml for metadata, remembering to accumulate or overwrite as we go along.
    216           for(int i = 0; i < search_files.size(); i++) {
    217                 MetadataXMLFileSearch a_search = (MetadataXMLFileSearch) search_files.get(i);
     214    }
     215    // Now search each of these metadata xml for metadata, remembering to accumulate or overwrite as we go along.
     216    for(int i = 0; i < search_files.size(); i++) {
     217        MetadataXMLFileSearch a_search = (MetadataXMLFileSearch) search_files.get(i);
    218218                ///ystem.err.println("Search " + a_search.file.getAbsolutePath() + File.separator + "metadata.xml for " + (a_search.filename != null ? a_search.filename : "directory metadata"));
    219219                // Retrieve the document
    220                 GDMDocument document = getDocument(a_search.file);
    221                 if(document != null) {
    222                      // There is one piece of slight of hand here. You can never remove metadata during a get all metadata.
    223                      metadata = document.getMetadata(a_search.filename, false, metadata, a_search.file);
    224                      ///ystem.err.println("Current metadata: " + toString(metadata));
    225                      document = null;
    226                 }
    227                 a_search = null;
    228           }
    229           start_file = null;
    230           collection_dir = null;
    231           filename = null;
    232           search_files.clear();
    233           search_files = null;
    234           return metadata;
    235    
    236 
    237     public String toString(ArrayList list) {
    238           StringBuffer text = new StringBuffer("(");
    239           for(int i = 0; list != null && i < list.size(); i++) {
    240                 text.append((list.get(i)).toString());
    241                 if(i < list.size() - 1) {
    242                      text.append(", ");
    243                 }
    244           }
    245           text.append(")");
    246           return text.toString();         
    247     }
    248 
    249     private class MetadataXMLFileSearch {
    250           public File file;
    251           public String filename;
    252           public MetadataXMLFileSearch(File file, String filename) {
    253                 this.file = file;
    254                 this.filename = filename;
    255           }
    256     }
    257 
    258     /** Called whenever the metadata value changes in some way, such as the addition of a new value. This is the only event type we care about, but we care about it a lot. It tells us what metadata to add, remove, etc from the cached metadata.xml files. Note that this method is synchronized so that the data objects don't need to be.
     220        GDMDocument document = getDocument(a_search.file);
     221        if(document != null) {
     222        // There is one piece of slight of hand here. You can never remove metadata during a get all metadata.
     223        metadata = document.getMetadata(a_search.filename, false, metadata, a_search.file);
     224        ///ystem.err.println("Current metadata: " + toString(metadata));
     225        document = null;
     226        }
     227        a_search = null;
     228    }
     229    start_file = null;
     230    collection_dir = null;
     231    filename = null;
     232    search_files.clear();
     233    search_files = null;
     234    return metadata;
     235   
     236
     237    public String toString(ArrayList list) {
     238    StringBuffer text = new StringBuffer("(");
     239    for(int i = 0; list != null && i < list.size(); i++) {
     240        text.append((list.get(i)).toString());
     241        if(i < list.size() - 1) {
     242        text.append(", ");
     243        }
     244    }
     245    text.append(")");
     246    return text.toString();       
     247    }
     248
     249    private class MetadataXMLFileSearch {
     250    public File file;
     251    public String filename;
     252    public MetadataXMLFileSearch(File file, String filename) {
     253        this.file = file;
     254        this.filename = filename;
     255    }
     256    }
     257
     258    /** Called whenever the metadata value changes in some way, such as the addition of a new value. This is the only event type we care about, but we care about it a lot. It tells us what metadata to add, remove, etc from the cached metadata.xml files. Note that this method is synchronized so that the data objects don't need to be.
    259259      * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    260260      * @see org.greenstone.gatherer.msm.GDMDocument
     
    262262      * @see org.greenstone.gatherer.util.HashMap3D
    263263      */
    264     public synchronized void metadataChanged(MSMEvent event) {
    265           System.err.println("Recieved Event: " + event.toString());
    266           File file = event.getFile();
    267           if(file == null) {
    268                 FileNode record = event.getRecord();
    269                 file = record.getFile();
    270           }
    271           Metadata new_metadata = event.getNewMetadata();
    272           Metadata old_metadata = event.getOldMetadata();
    273           // These metadata objects may be new instances of metadata objects that already exist. Replace them if they are.
    274           new_metadata = checkCache(new_metadata);
    275           old_metadata = checkCache(old_metadata);
    276           // Now apply the change to the document in question.
    277           GDMDocument metadata_xml = getDocument(file);
    278           if(metadata_xml != null) {
    279                 if(old_metadata != null) {
    280                      // File level
    281                      if(file.isFile()) {
    282                           metadata_xml.removeMetadata(file.getName(), old_metadata);
    283                      }
    284                      // Folder level
    285                      else {
    286                           metadata_xml.removeMetadata(null, old_metadata);
    287                      }
    288                 }
    289                 if(new_metadata != null) {
    290                      // File level
    291                      if(file.isFile()) {
    292                           metadata_xml.addMetadata(file.getName(), new_metadata);
    293                      }
    294                      else {
    295                           metadata_xml.addMetadata(null, new_metadata);
    296                      }
    297                 }
    298           }
    299     }
    300 
    301     public ArrayList removeMetadata(File file) {
    302           return getMetadata(file, true);
    303     }
    304 
    305     /** Causes all currently loaded GDMDocuments to write themselves out.
     264    public synchronized void metadataChanged(MSMEvent event) {
     265    System.err.println("Recieved Event: " + event.toString());
     266    File file = event.getFile();
     267    if(file == null) {
     268        FileNode record = event.getRecord();
     269        file = record.getFile();
     270    }
     271    Metadata new_metadata = event.getNewMetadata();
     272    Metadata old_metadata = event.getOldMetadata();
     273    // These metadata objects may be new instances of metadata objects that already exist. Replace them if they are.
     274    new_metadata = checkCache(new_metadata);
     275    old_metadata = checkCache(old_metadata);
     276    // Now apply the change to the document in question.
     277    GDMDocument metadata_xml = getDocument(file);
     278    if(metadata_xml != null) {
     279        if(old_metadata != null) {
     280        // File level
     281        if(file.isFile()) {
     282            metadata_xml.removeMetadata(file.getName(), old_metadata);
     283        }
     284        // Folder level
     285        else {
     286            metadata_xml.removeMetadata(null, old_metadata);
     287        }
     288        }
     289        if(new_metadata != null) {
     290        // File level
     291        if(file.isFile()) {
     292            metadata_xml.addMetadata(file.getName(), new_metadata);
     293        }
     294        else {
     295            metadata_xml.addMetadata(null, new_metadata);
     296        }
     297        }
     298    }
     299    }
     300
     301    public ArrayList removeMetadata(File file) {
     302    return getMetadata(file, true);
     303    }
     304
     305    /** Causes all currently loaded GDMDocuments to write themselves out.
    306306      * @see org.greenstone.gatherer.msm.GDMDocument
    307307      */
    308     public void save() {
    309           Iterator iterator = keySet().iterator();
    310           while(iterator.hasNext()) {
    311                 File file = (File) iterator.next();
    312                 GDMDocument document = (GDMDocument) get(file);
    313                 if(!document.isUpToDate()) {
    314                      // First purge any old references.
    315                      document.getMetadata(null, false, null, null, true);
    316                      // Now write the xml
    317                      Utility.export(document.getDocument(), file);
    318                      document.setUpToDate(true);
    319                 }
    320           }
    321     }
    322     /** Used to cause the document associated with a particular file to write the latest copy of itself to disk. */
    323     public void save(FileNode node) {
    324           File file = node.getFile();
    325           if(file != null && file.isFile()) {
    326                 GDMDocument document = getDocument(file);
    327                 File xml_file;
    328                 if(file.isFile()) {
    329                      xml_file = new File(file.getParentFile(), "metadata.xml");
    330                 }
    331                 else {
    332                      xml_file = new File(file, "metadata.xml");
    333                 }
    334                 if(document != null && !document.isUpToDate()) {
    335                      // First purge any old references.
    336                      document.getMetadata(null, false, null, null, true);
    337                      // Now write the xml
    338                      Utility.export(document.getDocument(), xml_file);
    339                      document.setUpToDate(true);
    340                 }
    341                 xml_file = null;
    342                 document = null;
    343           }
    344           file = null;
    345     }
    346 
    347     /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets. If a set changes, mark all cached GDMDocuments as being stale.
     308    public void save() {
     309    Iterator iterator = keySet().iterator();
     310    while(iterator.hasNext()) {
     311        File file = (File) iterator.next();
     312        GDMDocument document = (GDMDocument) get(file);
     313        if(!document.isUpToDate()) {
     314        // First purge any old references.
     315        document.getMetadata(null, false, null, null, true);
     316        // Now write the xml
     317        Utility.export(document.getDocument(), file);
     318        document.setUpToDate(true);
     319        }
     320    }
     321    }
     322    /** Used to cause the document associated with a particular file to write the latest copy of itself to disk. */
     323    public void save(FileNode node) {
     324    File file = node.getFile();
     325    if(file != null && file.isFile()) {
     326        GDMDocument document = getDocument(file);
     327        File xml_file;
     328        if(file.isFile()) {
     329        xml_file = new File(file.getParentFile(), "metadata.xml");
     330        }
     331        else {
     332        xml_file = new File(file, "metadata.xml");
     333        }
     334        if(document != null && !document.isUpToDate()) {
     335        // First purge any old references.
     336        document.getMetadata(null, false, null, null, true);
     337        // Now write the xml
     338        Utility.export(document.getDocument(), xml_file);
     339        document.setUpToDate(true);
     340        }
     341        xml_file = null;
     342        document = null;
     343    }
     344    file = null;
     345    }
     346
     347    /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets. If a set changes, mark all cached GDMDocuments as being stale.
    348348      * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    349349      */
    350     public void setChanged(MSMEvent event) {
    351           for(Iterator values = values().iterator(); values.hasNext(); ) {
    352                 GDMDocument document = (GDMDocument) values.next();
    353                 document.setUpToDate(false);
    354                 document = null;
    355           }
    356     }
    357     /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value. --While the comments below are now obsolete, I'll keep them just to remind me of how easy it is to back yourself into a corner with issues such as caching and persisitant references--. Such an action would require us to painstakingly reload every metadata.xml file using the value model prior to the change, then painstakingly write out each metadata.xml file again using the modified model, but I'm a glutton for punishment so thats ok. The alternative is to not do this and watch in horror as heirarchy references quickly fall into disarray, pointing to the wrong place. This task gets even more complicated by three facts:<br>1. We want to do this is a seperate thread, as we don't want the program to come to a screaming halt while we're updating metadata.xml files.<br>2. We have to prevent any metadata.xml files being removed from cache while we're doing this, as if we encounter these more recently written files their heirarchy references will already be correct and that will balls up our little process. Note that this means the saving process may have to block while pending metadata heirarchy updates are in progress.<br>3. Regarding (2) we don't have to rewrite any metadata.xml files already in cache as they will be correctly written out whenever they happen to be dumped from cache.<br>4. We need the ability to pre-empt the general update to load a user demanded metadata.xml and store it in cache, using the old value tree model as per usual, and<br>5. We have to store a cue of these events, and process them one at a time. Perhaps one day when I'm feeling masacistic I'll figure out someway to merge several updates into one, but for now we have to change the tree one node at a time in order for references to remain correct.<br>Ok, so thats five facts, but you get the gist. Not an easy task, but crucial for accurate storage and recall of metadata heirarchies.
     350    public void setChanged(MSMEvent event) {
     351    for(Iterator values = values().iterator(); values.hasNext(); ) {
     352        GDMDocument document = (GDMDocument) values.next();
     353        document.setUpToDate(false);
     354        document = null;
     355    }
     356    }
     357    /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value. --While the comments below are now obsolete, I'll keep them just to remind me of how easy it is to back yourself into a corner with issues such as caching and persisitant references--. Such an action would require us to painstakingly reload every metadata.xml file using the value model prior to the change, then painstakingly write out each metadata.xml file again using the modified model, but I'm a glutton for punishment so thats ok. The alternative is to not do this and watch in horror as heirarchy references quickly fall into disarray, pointing to the wrong place. This task gets even more complicated by three facts:<br>1. We want to do this is a seperate thread, as we don't want the program to come to a screaming halt while we're updating metadata.xml files.<br>2. We have to prevent any metadata.xml files being removed from cache while we're doing this, as if we encounter these more recently written files their heirarchy references will already be correct and that will balls up our little process. Note that this means the saving process may have to block while pending metadata heirarchy updates are in progress.<br>3. Regarding (2) we don't have to rewrite any metadata.xml files already in cache as they will be correctly written out whenever they happen to be dumped from cache.<br>4. We need the ability to pre-empt the general update to load a user demanded metadata.xml and store it in cache, using the old value tree model as per usual, and<br>5. We have to store a cue of these events, and process them one at a time. Perhaps one day when I'm feeling masacistic I'll figure out someway to merge several updates into one, but for now we have to change the tree one node at a time in order for references to remain correct.<br>Ok, so thats five facts, but you get the gist. Not an easy task, but crucial for accurate storage and recall of metadata heirarchies.
    358358      * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    359359      */
    360     public void valueChanged(MSMEvent event) {}
    361 
    362     public void waitUntilComplete() {
    363           gdm_loader.waitUntilComplete();
    364     }
    365 
    366     private Metadata checkCache(Metadata metadata) {
    367           if(metadata != null) {
     360    public void valueChanged(MSMEvent event) {}
     361
     362    public void waitUntilComplete() {
     363    gdm_loader.waitUntilComplete();
     364    }
     365
     366    private Metadata checkCache(Metadata metadata) {
     367    if(metadata != null) {
    368368                ///ystem.err.println("Search for " + metadata.toString());
    369                 if(metadata_cache.contains(metadata.getElement(), metadata.getValueNode())) {
    370                      metadata = (Metadata) metadata_cache.get(metadata.getElement(), metadata.getValueNode());
    371                 }
    372           }
    373           return metadata;
    374     }
    375 
    376     /** A separately threaded class to load all of the current metadata.xml files. Note that files can still be loaded on demand if they're not already in the cache. Also provides the functionality to block any other thread until the loading is complete, such as is necessary when moving values about in the value tree hierarchy. */
    377     private class GDMLoader
    378           extends Thread {
    379           private boolean complete = false;
    380           public void run() {
     369        if(metadata_cache.contains(metadata.getElement(), metadata.getValueNode())) {
     370        metadata = (Metadata) metadata_cache.get(metadata.getElement(), metadata.getValueNode());
     371        }
     372    }
     373    return metadata;
     374    }
     375
     376    /** A separately threaded class to load all of the current metadata.xml files. Note that files can still be loaded on demand if they're not already in the cache. Also provides the functionality to block any other thread until the loading is complete, such as is necessary when moving values about in the value tree hierarchy. */
     377    private class GDMLoader
     378    extends Thread {
     379    private boolean complete = false;
     380    public void run() {
    381381                // Can't open a collections metadata when the collection isn't open!
    382                 while(!Gatherer.c_man.ready()) {
    383                      try {
    384                           wait(100);
    385                      }
    386                      catch(Exception error) {
    387                      }
    388                 }
     382        while(!Gatherer.c_man.ready()) {
     383        try {
     384            wait(100);
     385        }
     386        catch(Exception error) {
     387        }
     388        }
    389389                // Now for each non-file directory in the tree, ask it to load its metadata
    390                 ArrayList remaining = new ArrayList();
    391                 remaining.add((FileNode)Gatherer.c_man.getRecordSet().getRoot());
    392                 int remaining_size = 0;
    393                 while((remaining_size = remaining.size()) > 0) {
    394                      FileNode record = (FileNode) remaining.remove(remaining_size - 1);
    395                      if(!record.isLeaf()) {
    396                           ///atherer.println("Retrieving metadata.xml for " + record);
    397                           getMetadata(record.getFile());
    398                           for(int i = 0; i < record.getChildCount(); i++) {
    399                                 remaining.add(record.getChildAt(i));
    400                           }
    401                      }
    402                      record = null;
    403                 }
    404                 remaining = null;
    405                 complete = true;
    406           }
    407           public void waitUntilComplete() {
    408                 try {
    409                      while(!complete) {
    410                           sleep(100); // 1 second hopefully.
    411                      }
    412                 }
    413                 catch(Exception error) {
    414                      Gatherer.printStackTrace(error);
    415                 }
    416           }
    417     }
     390        ArrayList remaining = new ArrayList();
     391        remaining.add((FileNode)Gatherer.c_man.getRecordSet().getRoot());
     392        int remaining_size = 0;
     393        while((remaining_size = remaining.size()) > 0) {
     394        FileNode record = (FileNode) remaining.remove(remaining_size - 1);
     395        if(!record.isLeaf()) {
     396            ///atherer.println("Retrieving metadata.xml for " + record);
     397            getMetadata(record.getFile());
     398            for(int i = 0; i < record.getChildCount(); i++) {
     399            remaining.add(record.getChildAt(i));
     400            }
     401        }
     402        record = null;
     403        }
     404        remaining = null;
     405        complete = true;
     406    }
     407    public void waitUntilComplete() {
     408        try {
     409        while(!complete) {
     410            sleep(100); // 1 second hopefully.
     411        }
     412        }
     413        catch(Exception error) {
     414        Gatherer.printStackTrace(error);
     415        }
     416    }
     417    }
    418418}
    419 
    420 
  • trunk/gli/src/org/greenstone/gatherer/msm/GDMParser.java

    r4293 r4365  
    7777// ####################################################################################
    7878public class GDMParser
    79     extends LinkedHashMap {
    80     /** A list of file names that we know do not actually belong to valid GDM xml files, so there not much point in ever trying to read them again. */
    81     private ArrayList ignore = null;
    82     /** The actual xerces parser used to read in xml documents. */
    83     private DOMParser parser = null;
    84     /** The default maximum cache size if max size not explicitly set. */
    85     private int max_size = 25;
    86     /** Default constructor. */
    87     public GDMParser() {
    88           super();
    89           this.ignore = new ArrayList();
    90           try {
    91                 parser = new DOMParser();
     79    extends LinkedHashMap {
     80    /** A list of file names that we know do not actually belong to valid GDM xml files, so there not much point in ever trying to read them again. */
     81    private ArrayList ignore = null;
     82    /** The actual xerces parser used to read in xml documents. */
     83    private DOMParser parser = null;
     84    /** The default maximum cache size if max size not explicitly set. */
     85    private int max_size = 25;
     86    /** Default constructor. */
     87    public GDMParser() {
     88    super();
     89    this.ignore = new ArrayList();
     90    try {
     91        parser = new DOMParser();
    9292                // Don't let it import external dtds. If it does it'll probably spit the dummy. If people try to use a poorly formated xml file more fool them.
    93                 parser.setFeature("http://xml.org/sax/features/validation", false);
    94                 parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
     93        parser.setFeature("http://xml.org/sax/features/validation", false);
     94        parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
    9595                // May or may not be ignored, the documentation for Xerces is contradictory. If it works then parsing -should- be faster.
    96                 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);
    97           }
    98           catch(Exception error) {
     96        parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);
     97    }
     98    catch(Exception error) {
    9999                ///ystem.err.println("Fatal Error in GDMParser.init(): " + error);
    100                 error.printStackTrace();
    101                 System.exit(1);
    102           }
    103     }
    104     /** Constructor with maximum size set.
     100        error.printStackTrace();
     101        System.exit(1);
     102    }
     103    }
     104    /** Constructor with maximum size set.
    105105      * @param max_size The maximum size of the cache, as an <i>int</i>.
    106106      */
    107     public GDMParser(int max_size) {
    108           this();
    109           this.max_size = max_size;
    110     }
    111     /** Destructor, clears cache and remove persistant global references. */
    112     public void destroy() {
    113           ignore.clear();
    114           ignore = null;
    115           parser = null;
    116           clear();
    117     }
    118     /** Fetches the document for the given xml file. This may mean (re)parsing it or simply fetching it from cache.
     107    public GDMParser(int max_size) {
     108    this();
     109    this.max_size = max_size;
     110    }
     111    /** Destructor, clears cache and remove persistant global references. */
     112    public void destroy() {
     113    ignore.clear();
     114    ignore = null;
     115    parser = null;
     116    clear();
     117    }
     118    /** Fetches the document for the given xml file. This may mean (re)parsing it or simply fetching it from cache.
    119119      * @param file The metadata.xml <strong>File</strong> you wish to get the document for.
    120120      * @return A <strong>Document</strong> which is sourced from file.
    121121      */
    122     public Document parse(File file) {
    123           ///ystem.err.println("Parse: " + file.getAbsolutePath());
    124           Document result = null;
    125           if(file.exists()) {
     122    public Document parse(File file) {
     123    ///ystem.err.println("Parse: " + file.getAbsolutePath());
     124    Document result = null;
     125    if(file.exists()) {
    126126                // Check if we've already parsed this file in an earlier attempt.
    127                 if(containsKey(file)) {
    128                      ///ystem.err.println("Already cached previously.");
    129                      //result = (Document) get(file);
    130                      SoftReference reference = (SoftReference) get(file);
    131                      if(reference != null) {
    132                           result = (Document) reference.get();
    133                      }
    134                      else {
    135                           ///ystem.err.println("Reference expired.");
    136                      }
    137                 }
     127        if(containsKey(file)) {
     128        ///ystem.err.println("Already cached previously.");
     129        //result = (Document) get(file);
     130        SoftReference reference = (SoftReference) get(file);
     131        if(reference != null) {
     132            result = (Document) reference.get();
     133        }
     134        else {
     135            ///ystem.err.println("Reference expired.");
     136        }
     137        }
    138138                // Check the ignore list and see if we've already detected this isn't a greenstone metadata file.
    139                 if(result == null && !ignore.contains(file)) {
    140                      ///ystem.err.println("Reparse file.");
    141                      // Of course we may not have, or it may have expired so...
    142                      try {
    143                           // Display progress dialog.
    144                           InputStream is = new FileInputStream(file);
    145                           InputStreamReader isr = new InputStreamReader(is);
    146                           Reader r = new BufferedReader(isr);
    147                           InputSource isc = new InputSource(r);
    148                           parser.parse(isc); // Slow.
    149                           Document document = parser.getDocument();
    150                           // First test. Check we have a GreenstoneDirectoryMetadata file, or for the older version DirectoryMetadata.
    151                           if(!document.getDoctype().getName().equals("GreenstoneDirectoryMetadata") && !document.getDoctype().getName().equals("DirectoryMetadata")) {
    152                                 ///ystem.err.println("Adding to ignore list.");
    153                                 // Add to ignore list. Not a gdm file.
    154                                 ignore.add(file);
    155                           }
    156                           // Cache document.
    157                           else {
    158                                 ///ystem.err.println("Adding to cache.");
    159                                 put(file, new SoftReference(document));
    160                                 result = document;
    161                           }
    162                      }
    163                      catch (Exception error) {
    164                           ///ystem.err.println("Error! " + error);
    165                           error.printStackTrace();
    166                      }
    167                 }
    168                 else {
    169                      ///ystem.err.println("File on ignore list.");
    170                 }
    171           }
    172           else {
     139        if(result == null && !ignore.contains(file)) {
     140        ///ystem.err.println("Reparse file.");
     141        // Of course we may not have, or it may have expired so...
     142        try {
     143            // Display progress dialog.
     144            InputStream is = new FileInputStream(file);
     145            InputStreamReader isr = new InputStreamReader(is);
     146            Reader r = new BufferedReader(isr);
     147            InputSource isc = new InputSource(r);
     148            parser.parse(isc); // Slow.
     149            Document document = parser.getDocument();
     150            // First test. Check we have a GreenstoneDirectoryMetadata file, or for the older version DirectoryMetadata.
     151            if(!document.getDoctype().getName().equals("GreenstoneDirectoryMetadata") && !document.getDoctype().getName().equals("DirectoryMetadata")) {
     152            ///ystem.err.println("Adding to ignore list.");
     153            // Add to ignore list. Not a gdm file.
     154            ignore.add(file);
     155            }
     156            // Cache document.
     157            else {
     158            ///ystem.err.println("Adding to cache.");
     159            put(file, new SoftReference(document));
     160            result = document;
     161            }
     162        }
     163        catch (Exception error) {
     164            ///ystem.err.println("Error! " + error);
     165            error.printStackTrace();
     166        }
     167        }
     168        else {
     169        ///ystem.err.println("File on ignore list.");
     170        }
     171    }
     172    else {
    173173                ///ystem.err.println("File does not exist!");
    174           }
    175           return result;
    176     }
    177     /** Automatically called by the LinkedHashMap object whenever an object is added, to determine whether it should remove the oldest entry.
     174    }
     175    return result;
     176    }
     177    /** Automatically called by the LinkedHashMap object whenever an object is added, to determine whether it should remove the oldest entry.
    178178      * @param eldest The eldest <strong>Map.Entry</strong> which may mean in terms of age, or in terms of usage.
    179179      * @return <i>true</i> if the given entry should be removed, <i>false</i> otherwise.
    180180      */
    181     protected boolean removeEldestEntry(Map.Entry eldest) {
    182           return size() > max_size;
    183     }
     181    protected boolean removeEldestEntry(Map.Entry eldest) {
     182    return size() > max_size;
     183    }
    184184}
  • trunk/gli/src/org/greenstone/gatherer/msm/GreenstoneArchiveParser.java

    r4319 r4365  
    5656public class GreenstoneArchiveParser {
    5757
    58     private GShell shell;
     58    private GShell shell;
    5959
    60     static final String ignore_list[] = {"assocfilepath","gsdl","Identifier","Source","URL"};
     60    static final String ignore_list[] = {"assocfilepath","gsdl","Identifier","Source","URL"};
    6161
    62     public GreenstoneArchiveParser(GShellProgressMonitor progress, GShell shell) {
    63           // We can only extract metadata if an extracted metadata set exists in our collection.
    64           if(Gatherer.c_man.msm.getSet("") != null) {
    65                 this.shell = shell;
     62    public GreenstoneArchiveParser(GShellProgressMonitor progress, GShell shell) {
     63    // We can only extract metadata if an extracted metadata set exists in our collection.
     64    if(Gatherer.c_man.msm.getSet("") != null) {
     65        this.shell = shell;
    6666                // Determine the collection archive directory.
    67                 File archive_directory = new File(Gatherer.c_man.getCollectionArchive());
     67        File archive_directory = new File(Gatherer.c_man.getCollectionArchive());
    6868                // For each of the hash coded directories within.
    69                 File document_directories[] = archive_directory.listFiles();
    70                 for(int i = 0; i < document_directories.length; i++) {
    71                      // Find the doc.xml file within
    72                      if(document_directories[i].isDirectory()) {
    73                           File document_file = new File(document_directories[i], "doc.xml");
    74                           // Then extract the metadata from it.
    75                           if(document_file.exists()) {
    76                                 extractMetadata(document_file);
    77                                 // Display a pretty progress message.
    78                                 shell.fireMessage(GShell.IMPORT, shell.typeAsString(GShell.IMPORT) + "> " + Gatherer.dictionary.get("GShell.Extracted", document_directories[i].getName()), GShell.OK);
    79                                 progress.increment();
    80                           }
    81                      }
     69        File document_directories[] = archive_directory.listFiles();
     70        for(int i = 0; i < document_directories.length; i++) {
     71        // Find the doc.xml file within
     72        if(document_directories[i].isDirectory()) {
     73            File document_file = new File(document_directories[i], "doc.xml");
     74            // Then extract the metadata from it.
     75            if(document_file.exists()) {
     76            extractMetadata(document_file);
     77            // Display a pretty progress message.
     78            shell.fireMessage(GShell.IMPORT, shell.typeAsString(GShell.IMPORT) + "> " + Gatherer.dictionary.get("GShell.Extracted", document_directories[i].getName()), GShell.OK);
     79            progress.increment();
     80            }
     81        }
     82        }
     83    }
     84    // All done. Outta here like a bald man.
     85    }
     86
     87    private void extractMetadata(File file) {
     88    // Retrieve the DOM of the file.
     89    Document document = Utility.parse(file, false);
     90    // If we successfully parsed the document, then it is time to search through the DOM for the Metadata tags.
     91    if(document != null) {
     92        String file_path = null;
     93        Element archive_element = document.getDocumentElement();
     94                // Retrieve all of the Metadata sections.
     95        NodeList metadata_elements = archive_element.getElementsByTagName("Metadata");
     96                // Now for each Metadata entry retrieved...
     97        for(int i = 0; i < metadata_elements.getLength(); i++) {
     98        Element metadata_element = (Element) metadata_elements.item(i);
     99        String name = metadata_element.getAttribute("name");
     100        // There is a special case when the metadata name is gsdlsourcefilename, as we use this to find the FileRecord we want to add metadata to.
     101        if(name.equals("gsdlsourcefilename")) {
     102            file_path = MSMUtils.getValue(metadata_element);
     103        }
     104        else {
     105            // Check if its name starts with, or is equal to, one of the values in our ignore list, and if so ignore this metadata.
     106            boolean ignore = (name.indexOf(".") != -1);
     107            for(int j = 0; !ignore && j < ignore_list.length; j++) {
     108            ignore = name.startsWith(ignore_list[j]);
     109            }
     110            // Otherwise ensure the metadata is present in our collection.
     111            if(!ignore && file_path != null) {
     112            // If we successfully retrieved a record we can continue.
     113            if(file_path != null) {
     114                // We now retrieve the appropriate element. If no such element exists we create a new one in the greenstone mds. Remember that no element in the greenstone mds has an associated value tree, so it is perfect for metadata elements with a small number of repeated values but where the values have no relation between files (such as encoding, where many files will be iso_8859_1, but if you change one you don't intend to change them all).
     115                ElementWrapper element = Gatherer.c_man.msm.getElement(name);
     116                if(element == null) {
     117                MetadataSet extracted_mds = Gatherer.c_man.msm.getSet("ex");
     118                element = extracted_mds.addElement(name);
     119                }
     120                // If we successfully retrieved an element (and we should have) we can continue.
     121                if(element != null) {
     122                // Retrieve the metadata for the current file
     123                File target_file = new File(file_path);
     124                ArrayList metadatum = Gatherer.c_man.getCollection().gdm.getMetadata(target_file);
     125                // If no metadata exists for the current element, add it
     126                boolean found = false;
     127                for(int k = 0; !found && k < metadatum.size(); k++) {
     128                    Metadata sibling = (Metadata) metadatum.get(k);
     129                    found = element.equals(sibling.getElement());
    82130                }
    83           }
    84           // All done. Outta here like a bald man.
    85      }
    86 
    87      private void extractMetadata(File file) {
    88           // Retrieve the DOM of the file.
    89           Document document = Utility.parse(file, false);
    90           // If we successfully parsed the document, then it is time to search through the DOM for the Metadata tags.
    91           if(document != null) {
    92                 String file_path = null;
    93                 Element archive_element = document.getDocumentElement();
    94                 // Retrieve all of the Metadata sections.
    95                 NodeList metadata_elements = archive_element.getElementsByTagName("Metadata");
    96                 // Now for each Metadata entry retrieved...
    97                 for(int i = 0; i < metadata_elements.getLength(); i++) {
    98                      Element metadata_element = (Element) metadata_elements.item(i);
    99                      String name = metadata_element.getAttribute("name");
    100                      // There is a special case when the metadata name is gsdlsourcefilename, as we use this to find the FileRecord we want to add metadata to.
    101                      if(name.equals("gsdlsourcefilename")) {
    102                           file_path = MSMUtils.getValue(metadata_element);
    103                      }
    104                      else {
    105                           // Check if its name starts with, or is equal to, one of the values in our ignore list, and if so ignore this metadata.
    106                           boolean ignore = (name.indexOf(".") != -1);
    107                           for(int j = 0; !ignore && j < ignore_list.length; j++) {
    108                                 ignore = name.startsWith(ignore_list[j]);
    109                           }
    110                           // Otherwise ensure the metadata is present in our collection.
    111                           if(!ignore && file_path != null) {
    112                                 // If we successfully retrieved a record we can continue.
    113                                 if(file_path != null) {
    114                                      // We now retrieve the appropriate element. If no such element exists we create a new one in the greenstone mds. Remember that no element in the greenstone mds has an associated value tree, so it is perfect for metadata elements with a small number of repeated values but where the values have no relation between files (such as encoding, where many files will be iso_8859_1, but if you change one you don't intend to change them all).
    115                                      ElementWrapper element = Gatherer.c_man.msm.getElement(name);
    116                                      if(element == null) {
    117                                           MetadataSet extracted_mds = Gatherer.c_man.msm.getSet("ex");
    118                                           element = extracted_mds.addElement(name);
    119                                      }
    120                                      // If we successfully retrieved an element (and we should have) we can continue.
    121                                      if(element != null) {
    122                                           // Retrieve the metadata for the current file
    123                                           File target_file = new File(file_path);
    124                                           ArrayList metadatum = Gatherer.c_man.getCollection().gdm.getMetadata(target_file);
    125                                           // If no metadata exists for the current element, add it
    126                                           boolean found = false;
    127                                           for(int k = 0; !found && k < metadatum.size(); k++) {
    128                                                 Metadata sibling = (Metadata) metadatum.get(k);
    129                                                 found = element.equals(sibling.getElement());
    130                                           }
    131                                           metadatum = null;
    132                                           if(!found) {
    133                                                 String value = "";
    134                                                 try {
    135                                                      value = Utility.decodeGreenstone(URLDecoder.decode(MSMUtils.getValue(metadata_element), "UTF-8"));
    136                                                 }
    137                                                 catch(UnsupportedEncodingException error) {
    138                                                      Gatherer.printStackTrace(error);
    139                                                 }
    140                                               // If we successfully retrieved a value we can continue.
    141                                                 if(value != null) {
    142                                                      // Create a new metadata object.
    143                                                      GValueModel value_tree = Gatherer.c_man.msm.getValueTree(element);
    144                                                      GValueNode value_node = null;
    145                                                      if(value_tree != null) {
    146                                                           value_node = value_tree.getValue(value);
    147                                                      }
    148                                                      else {
    149                                                           value_node = new GValueNode(element.toString(), value);
    150                                                      }
    151                                                      Metadata metadata = new Metadata(element, value_node);
    152                                                      Gatherer.c_man.getCollection().gdm.metadataChanged(new MSMEvent(this, System.currentTimeMillis(), target_file, null, metadata));
    153                                                      // All done. On to next metadata.
    154                                                 }
    155                                           }
    156                                           target_file = null;
    157                                      }
    158                                      else {
    159                                           Gatherer.println("Cannot retrieve metadata element " + name);
    160                                      }
    161                                 }
    162                           }
    163                      }
     131                metadatum = null;
     132                if(!found) {
     133                    String value = "";
     134                    try {
     135                    value = Utility.decodeGreenstone(URLDecoder.decode(MSMUtils.getValue(metadata_element), "UTF-8"));
     136                    }
     137                    catch(UnsupportedEncodingException error) {
     138                    Gatherer.printStackTrace(error);
     139                    }
     140                    // If we successfully retrieved a value we can continue.
     141                    if(value != null) {
     142                    // Create a new metadata object.
     143                    GValueModel value_tree = Gatherer.c_man.msm.getValueTree(element);
     144                    GValueNode value_node = null;
     145                    if(value_tree != null) {
     146                        value_node = value_tree.getValue(value);
     147                    }
     148                    else {
     149                        value_node = new GValueNode(element.toString(), value);
     150                    }
     151                    Metadata metadata = new Metadata(element, value_node);
     152                    Gatherer.c_man.getCollection().gdm.metadataChanged(new MSMEvent(this, System.currentTimeMillis(), target_file, null, metadata));
     153                    // All done. On to next metadata.
     154                    }
    164155                }
    165           }
    166      }
     156                target_file = null;
     157                }
     158                else {
     159                Gatherer.println("Cannot retrieve metadata element " + name);
     160                }
     161            }
     162            }
     163        }
     164        }
     165    }
     166    }
    167167}
  • trunk/gli/src/org/greenstone/gatherer/msm/MDSFileFilter.java

    r4293 r4365  
    4141/** A custom <strong>FileFilter</strong> for the file choosers. */
    4242final public class MDSFileFilter
    43     extends javax.swing.filechooser.FileFilter {
    44     private String description = null;
    45     /** Constructor.
    46       * @see org.greenstone.gatherer.Dictionary
    47       */
    48     public MDSFileFilter() {
    49           description = Gatherer.dictionary.get("MSMPrompt.File_Filter_Description");
    50     }
    51     /** Override this method to return <i>true</i> only if the file extension is .mds.
    52       * @param f A possible mds <strong>File</strong>.
    53       * @return <i>true</i> if we should show this file, <i>false</i> otherwise.
    54       */
    55     public boolean accept(File f) {
    56           String file_name = f.getName();
    57           file_name = file_name.toLowerCase();
    58           if(file_name.endsWith(".mds") || file_name.indexOf(".") == -1) {
    59                 return true;
    60           }
    61           return false;
    62     }
    63     /** Retrieve the description for this filter.
    64       * @return The description as a <strong>String</strong>.
    65       */
    66     public String getDescription() {
    67           return description;
    68     }
     43    extends javax.swing.filechooser.FileFilter {
     44    private String description = null;
     45    /** Constructor.
     46     * @see org.greenstone.gatherer.Dictionary
     47     */
     48    public MDSFileFilter() {
     49    description = Gatherer.dictionary.get("MSMPrompt.File_Filter_Description");
     50    }
     51    /** Override this method to return <i>true</i> only if the file extension is .mds.
     52     * @param f A possible mds <strong>File</strong>.
     53     * @return <i>true</i> if we should show this file, <i>false</i> otherwise.
     54     */
     55    public boolean accept(File f) {
     56    String file_name = f.getName();
     57    file_name = file_name.toLowerCase();
     58    if(file_name.endsWith(".mds") || file_name.indexOf(".") == -1) {
     59        return true;
     60    }
     61    return false;
     62    }
     63    /** Retrieve the description for this filter.
     64     * @return The description as a <strong>String</strong>.
     65     */
     66    public String getDescription() {
     67    return description;
     68    }
    6969}
  • trunk/gli/src/org/greenstone/gatherer/msm/MSMAction.java

    r4293 r4365  
    5555/** This class provides all the information required to correctly perform metadata actions, with regard to the users previously indicated preferences in terms of importing and renaming. */
    5656final public class MSMAction {
    57     private boolean hfile = false;
    58     /** The type of action required in subsequent actions involving the specified set and source element. */
    59     private int action = Declarations.NO_ACTION;
    60     private String set = null;
    61     private String source = null;
    62     /** The fully qualified name of the target element (which must be currently in the loaded metadata sets, otherwise the set it belongs to cannot be determined. */
    63     private String target = null;
    64     /** Constructor. Note that the standard action FORCE_MERGE is only named so for clarity and is translated into its actual action RENAME when this profile is created.
    65       * @param action An <i>int</i> representing what action needs to be taken in subsequent access of the given set and source element.
    66       * @param target A <strong>String</strong> representing the fully qualified name of the target element.
    67       * @param hfile If this action was created from the hfile parsing stage, does the element require a replacement to change its alias value into a real one. Note that this profile item may be created for fully namespaced elements as well, to ensure aliases are always remapped.
    68       */
    69     public MSMAction(int action, String target) {
    70           this.target = target;
    71           switch(action) {
    72           case Declarations.DELETE:
    73                 this.action = action;
    74                 break;
    75           case Declarations.FORCE_MERGE:
    76                 this.action = Declarations.RENAME;
    77                 break;
    78           case Declarations.RENAME:
    79                 this.action = action;
    80                 break;
    81           case Declarations.SKIP:
    82                 this.action = action;
    83                 this.target = null;
    84                 break;
    85           default:
    86                 this.action = Declarations.NO_ACTION;
    87           }
    88     }
    89     /** Constructor. Note that the standard action FORCE_MERGE is only named so for clarity and is translated into its actual action RENAME when this profile is created.
    90       * @param action An <i>int</i> representing what action needs to be taken in subsequent access of the given set and source element.
    91       * @param target A <strong>String</strong> representing the fully qualified name of the target element.
    92       * @param hfile If this action was created from the hfile parsing stage, does the element require a replacement to change its alias value into a real one. Note that this profile item may be created for fully namespaced elements as well, to ensure aliases are always remapped.
    93       */
    94     public MSMAction(int action, String target, boolean hfile) {
    95           this.hfile = hfile;
    96           this.target = target;
    97           switch(action) {
    98           case Declarations.FORCE_MERGE:
    99                 this.action = Declarations.RENAME;
    100                 break;
    101           case Declarations.RENAME:
    102                 this.action = action;
    103                 break;
    104           case Declarations.SKIP:
    105                 this.action = action;
    106                 this.target = null;
    107                 break;
    108           default:
    109                 this.action = Declarations.NO_ACTION;
    110           }
    111     }
     57    private boolean hfile = false;
     58    /** The type of action required in subsequent actions involving the specified set and source element. */
     59    private int action = Declarations.NO_ACTION;
     60    private String set = null;
     61    private String source = null;
     62    /** The fully qualified name of the target element (which must be currently in the loaded metadata sets, otherwise the set it belongs to cannot be determined. */
     63    private String target = null;
     64    /** Constructor. Note that the standard action FORCE_MERGE is only named so for clarity and is translated into its actual action RENAME when this profile is created.
     65     * @param action An <i>int</i> representing what action needs to be taken in subsequent access of the given set and source element.
     66     * @param target A <strong>String</strong> representing the fully qualified name of the target element.
     67     * @param hfile If this action was created from the hfile parsing stage, does the element require a replacement to change its alias value into a real one. Note that this profile item may be created for fully namespaced elements as well, to ensure aliases are always remapped.
     68     */
     69    public MSMAction(int action, String target) {
     70    this.target = target;
     71    switch(action) {
     72    case Declarations.DELETE:
     73        this.action = action;
     74        break;
     75    case Declarations.FORCE_MERGE:
     76        this.action = Declarations.RENAME;
     77        break;
     78    case Declarations.RENAME:
     79        this.action = action;
     80        break;
     81    case Declarations.SKIP:
     82        this.action = action;
     83        this.target = null;
     84        break;
     85    default:
     86        this.action = Declarations.NO_ACTION;
     87    }
     88    }
     89    /** Constructor. Note that the standard action FORCE_MERGE is only named so for clarity and is translated into its actual action RENAME when this profile is created.
     90     * @param action An <i>int</i> representing what action needs to be taken in subsequent access of the given set and source element.
     91     * @param target A <strong>String</strong> representing the fully qualified name of the target element.
     92     * @param hfile If this action was created from the hfile parsing stage, does the element require a replacement to change its alias value into a real one. Note that this profile item may be created for fully namespaced elements as well, to ensure aliases are always remapped.
     93     */
     94    public MSMAction(int action, String target, boolean hfile) {
     95    this.hfile = hfile;
     96    this.target = target;
     97    switch(action) {
     98    case Declarations.FORCE_MERGE:
     99        this.action = Declarations.RENAME;
     100        break;
     101    case Declarations.RENAME:
     102        this.action = action;
     103        break;
     104    case Declarations.SKIP:
     105        this.action = action;
     106        this.target = null;
     107        break;
     108    default:
     109        this.action = Declarations.NO_ACTION;
     110    }
     111    }
    112112
    113     public MSMAction(String set, String source, int action, String target) {
    114           this(action, target);
    115           this.set = set;
    116           this.source = source;
    117     }
     113    public MSMAction(String set, String source, int action, String target) {
     114    this(action, target);
     115    this.set = set;
     116    this.source = source;
     117    }
    118118
    119     /** Method for retrieving the appropriate action from this class.
    120       * @return An <i>int</i> specifying a particular action.
    121       */
    122     public int getAction() {
    123           return action;
    124     }
    125     public String getSet() {
    126           return set;
    127     }
    128     public String getSource() {
    129           return source;
    130     }
    131     /** Method for retrieving the target element of this action.
    132       * @return A <strong>String</strong> stating the fully qualified name of the target element, within the namespace of one of the currently loaded metadata sets.
    133       */
    134     public String getTarget() {
    135           return target;
    136     }
    137     public boolean isHFile() {
    138           return hfile;
    139     }
     119    /** Method for retrieving the appropriate action from this class.
     120     * @return An <i>int</i> specifying a particular action.
     121     */
     122    public int getAction() {
     123    return action;
     124    }
     125    public String getSet() {
     126    return set;
     127    }
     128    public String getSource() {
     129    return source;
     130    }
     131    /** Method for retrieving the target element of this action.
     132     * @return A <strong>String</strong> stating the fully qualified name of the target element, within the namespace of one of the currently loaded metadata sets.
     133     */
     134    public String getTarget() {
     135    return target;
     136    }
     137    public boolean isHFile() {
     138    return hfile;
     139    }
    140140}
  • trunk/gli/src/org/greenstone/gatherer/msm/MSMAdapter.java

    r4293 r4365  
    5757/** The default adapter for a MSMListener doesn't actually do anything, thus users can override whatever methods they want. */
    5858public class MSMAdapter
    59      implements MSMListener {
    60      /** Method that is called whenever an element within a set is changed or modified.
     59    implements MSMListener {
     60    /** Method that is called whenever an element within a set is changed or modified.
     61     * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
     62      */
     63    public void elementChanged(MSMEvent event){}
     64    /** Called whenever the metadata value changes in some way, such as the addition of a new value.
    6165      * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    6266      */
    63      public void elementChanged(MSMEvent event){}
    64      /** Called whenever the metadata value changes in some way, such as the addition of a new value.
     67    public void metadataChanged(MSMEvent event){}
     68    /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets.
    6569      * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    6670      */
    67      public void metadataChanged(MSMEvent event){}
    68      /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets.
     71    public void setChanged(MSMEvent event){}
     72    /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value.
    6973      * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    7074      */
    71      public void setChanged(MSMEvent event){}
    72      /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value.
    73       * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    74       */
    75      public void valueChanged(MSMEvent event){}
     75    public void valueChanged(MSMEvent event){}
    7676}
  • trunk/gli/src/org/greenstone/gatherer/msm/MSMEvent.java

    r4361 r4365  
    5959/** An event object which contains useful information about the event that generated it, which in this case is probably an element update (this means a MSMAction object should be provided). */
    6060public class MSMEvent
    61     extends AWTEvent {
    62     /** The element associated with this event. */
    63     private ElementWrapper element = null;
    64     /** The file associated with this event. */
    65     private File file = null;
    66     /** The record associated with this event. */
    67     private FileNode record = null;
    68     /** The value tree model after adding a new value. */
    69     private GValueModel new_model = null;
    70     /** The value tree model prior to adding a new value. */
    71     private GValueModel old_model = null;
    72     /** A unique id given to any file or metadata task so it can be undone in its entirity. */
    73     private long id;
    74     /** The new metadata associated with this event. */
    75     private Metadata new_metadata = null;
    76     /** The old metadata associated with this event. */
    77     private Metadata old_metadata = null;
    78     /** The profile associated with this event. */
    79     private MSMAction profile = null;
    80     /** The value associated with this event. */
    81     private String value = null;
    82     /** The message counter. */
    83     static private int count = 0;
    84     /** Constructor.
    85       * @param source The <strong>Object</strong> that caused this event.
    86       * @param id An <i>long</i> identifier.
    87       * @param element The <strong>ElementWrapper</strong> affected.
    88       */
    89     public MSMEvent(Object source, long id, ElementWrapper element, GValueModel old_model, GValueModel new_model) {
    90           super(source, 0);
    91           this.element = element;
    92           this.id = id;
    93           this.new_model = new_model;
    94           this.old_model = old_model;
    95           this.profile = null;
    96           this.record = null;
    97     }
    98     /** Constructor for a metadata change event involving a file. */
    99     public MSMEvent(Object source, long id, File file, Metadata old_metadata, Metadata new_metadata) {
    100           super(source, 0);
    101           this.element = null;
    102           this.file = file;
    103           this.id = id;
    104           this.new_metadata = new_metadata;
    105           this.old_metadata = old_metadata;
    106           this.profile = null;
    107     }
    108     /** Constructor for a metadata change event involving a filenode. */
    109     public MSMEvent(Object source, long id, FileNode record, Metadata old_metadata, Metadata new_metadata) {
    110           super(source, 0);
    111           this.element = null;
    112           this.id = id;
    113           this.new_metadata = new_metadata;
    114           this.old_metadata = old_metadata;
    115           this.record = record;
    116           this.profile = null;
    117     }
    118     /** Constructor.
     61    extends AWTEvent {
     62    /** The element associated with this event. */
     63    private ElementWrapper element = null;
     64    /** The file associated with this event. */
     65    private File file = null;
     66    /** The record associated with this event. */
     67    private FileNode record = null;
     68    /** The value tree model after adding a new value. */
     69    private GValueModel new_model = null;
     70    /** The value tree model prior to adding a new value. */
     71    private GValueModel old_model = null;
     72    /** A unique id given to any file or metadata task so it can be undone in its entirity. */
     73    private long id;
     74    /** The new metadata associated with this event. */
     75    private Metadata new_metadata = null;
     76    /** The old metadata associated with this event. */
     77    private Metadata old_metadata = null;
     78    /** The profile associated with this event. */
     79    private MSMAction profile = null;
     80    /** The value associated with this event. */
     81    private String value = null;
     82    /** The message counter. */
     83    static private int count = 0;
     84    /** Constructor.
     85     * @param source The <strong>Object</strong> that caused this event.
     86     * @param id An <i>long</i> identifier.
     87     * @param element The <strong>ElementWrapper</strong> affected.
     88      */
     89    public MSMEvent(Object source, long id, ElementWrapper element, GValueModel old_model, GValueModel new_model) {
     90    super(source, 0);
     91    this.element = element;
     92    this.id = id;
     93    this.new_model = new_model;
     94    this.old_model = old_model;
     95    this.profile = null;
     96    this.record = null;
     97    }
     98    /** Constructor for a metadata change event involving a file. */
     99    public MSMEvent(Object source, long id, File file, Metadata old_metadata, Metadata new_metadata) {
     100    super(source, 0);
     101    this.element = null;
     102    this.file = file;
     103    this.id = id;
     104    this.new_metadata = new_metadata;
     105    this.old_metadata = old_metadata;
     106    this.profile = null;
     107    }
     108    /** Constructor for a metadata change event involving a filenode. */
     109    public MSMEvent(Object source, long id, FileNode record, Metadata old_metadata, Metadata new_metadata) {
     110    super(source, 0);
     111    this.element = null;
     112    this.id = id;
     113    this.new_metadata = new_metadata;
     114    this.old_metadata = old_metadata;
     115    this.record = record;
     116    this.profile = null;
     117    }
     118    /** Constructor.
    119119      * @param source The <strong>Object</strong> that caused this event.
    120120      * @param id An <i>long</i> identifier.
    121121      * @param profile The <strong>MSMAction</strong> profile if one is applicable.
    122122      */
    123     public MSMEvent(Object source, long id, MSMAction profile) {
    124           super(source, 00);
    125           this.element = null;
    126           this.id = id;
    127           this.record = null;
    128           this.profile = profile;
    129     }
    130     /** Constructor for a metadata element changed event.
     123    public MSMEvent(Object source, long id, MSMAction profile) {
     124    super(source, 00);
     125    this.element = null;
     126    this.id = id;
     127    this.record = null;
     128    this.profile = profile;
     129    }
     130    /** Constructor for a metadata element changed event.
    131131      * @param source The <strong>Object</strong> that caused this event.
    132132      * @param element A reference to the effected metadata <strong>ElementWrapper</strong>, or <i>null</i> if element no longer exists.
    133133      * @param value The old name of the element (if its name has changed). If <i>null</i> old name defaults to element.toString().
    134134      */
    135     public MSMEvent(Object source, ElementWrapper element, String value) {
    136           super(source, count++);
    137           this.element = element;
    138           this.value = value;
    139     }
    140     /** Method to retrieve of the element associated with this event.
     135    public MSMEvent(Object source, ElementWrapper element, String value) {
     136    super(source, count++);
     137    this.element = element;
     138    this.value = value;
     139    }
     140    /** Method to retrieve of the element associated with this event.
    141141      * @return A <strong>ElementWrapper</strong>, or <i>null</i> if there is none.
    142142      */
    143     public ElementWrapper getElement() {
    144           return element;
    145     }
    146 
    147     public File getFile() {
    148           return file;
    149     }
    150     /** Method to retrieve the new metadata associated with this event.
     143    public ElementWrapper getElement() {
     144    return element;
     145    }
     146
     147    public File getFile() {
     148    return file;
     149    }
     150    /** Method to retrieve the new metadata associated with this event.
    151151      * @return A <strong>Metadata</strong>, or <i>null</i> if there is none.
    152152      */
    153     public Metadata getNewMetadata() {
    154           return new_metadata;
    155     }
    156     public GValueModel getNewModel() {
    157           return new_model;
    158     }
    159     /** Method to retrieve the old metadata associated with this event.
     153    public Metadata getNewMetadata() {
     154    return new_metadata;
     155    }
     156    public GValueModel getNewModel() {
     157    return new_model;
     158    }
     159    /** Method to retrieve the old metadata associated with this event.
    160160      * @return A <strong>Metadata</strong>, or <i>null</i> if there is none.
    161161      */
    162     public Metadata getOldMetadata() {
    163           return old_metadata;
    164     }
    165     public GValueModel getOldModel() {
    166           return old_model;
    167     }
    168     /** Method to retrieve of the profile associated with this event.
     162    public Metadata getOldMetadata() {
     163    return old_metadata;
     164    }
     165    public GValueModel getOldModel() {
     166    return old_model;
     167    }
     168    /** Method to retrieve of the profile associated with this event.
    169169      * @return A <strong>MSMAction</strong> which details the action profile, or <i>null</i> if no profile exists.
    170170      */
    171     public MSMAction getProfile() {
    172           return profile;
    173     }
    174     /** Retrieve the record associated with this event. */
    175     public FileNode getRecord() {
    176           return record;
    177     }
    178     /** Retrieve the associated with this event.
     171    public MSMAction getProfile() {
     172    return profile;
     173    }
     174    /** Retrieve the record associated with this event. */
     175    public FileNode getRecord() {
     176    return record;
     177    }
     178    /** Retrieve the associated with this event.
    179179      * @return A <strong>String</strong>.
    180180      */
    181     public String getValue() {
    182           String result = null;
    183           if(value != null) {
    184                 result = value;
    185           }
    186           else if(element != null) {
    187                 result = element.toString();
    188           }
    189           return result;
    190     }
    191     public long ID() {
    192           return id;
    193     }
    194 
    195     public String toString() {
    196           StringBuffer text = new StringBuffer("");
    197           if(old_metadata != null) {
    198                 text.append(old_metadata.toString());
    199           }
    200           text.append(" => ");
    201           if(new_metadata != null) {
    202                 text.append(new_metadata.toString());
    203           }
    204           return text.toString();
    205     }
     181    public String getValue() {
     182    String result = null;
     183    if(value != null) {
     184        result = value;
     185    }
     186    else if(element != null) {
     187        result = element.toString();
     188    }
     189    return result;
     190    }
     191    public long ID() {
     192    return id;
     193    }
     194
     195    public String toString() {
     196    StringBuffer text = new StringBuffer("");
     197    if(old_metadata != null) {
     198        text.append(old_metadata.toString());
     199    }
     200    text.append(" => ");
     201    if(new_metadata != null) {
     202        text.append(new_metadata.toString());
     203    }
     204    return text.toString();
     205    }
    206206}
  • trunk/gli/src/org/greenstone/gatherer/msm/MSMListener.java

    r4293 r4365  
    5353/** This class provides an interface for classes who wish to listener for events within the <strong>MetadataSetManager</strong>. There are events which are fired whenever a set or an element within a set changes. The events are either very broad, of the <i>metadataSetChanged()</i> variaty down to the more explicit <i>elementChanged(Profile change)</i> which actually details what change occured so the GUI etc can update just that element. */
    5454public interface MSMListener {
    55     /** Method that is called whenever an element within a set is changed or modified.
    56       * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    57       */
    58     public void elementChanged(MSMEvent event);
    59     /** Called whenever the metadata value changes in some way, such as the addition of a new value.
    60       * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    61       */
    62     public void metadataChanged(MSMEvent event);
    63     /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets.
    64       * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    65       */
    66     public void setChanged(MSMEvent event);
    67     /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value.
    68       * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    69       */
    70     public void valueChanged(MSMEvent event);
     55    /** Method that is called whenever an element within a set is changed or modified.
     56     * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
     57     */
     58    public void elementChanged(MSMEvent event);
     59    /** Called whenever the metadata value changes in some way, such as the addition of a new value.
     60     * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
     61     */
     62    public void metadataChanged(MSMEvent event);
     63    /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets.
     64     * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
     65     */
     66    public void setChanged(MSMEvent event);
     67    /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value.
     68     * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
     69     */
     70    public void valueChanged(MSMEvent event);
    7171}
  • trunk/gli/src/org/greenstone/gatherer/msm/MSMListenerAdapter.java

    r4293 r4365  
    5454/** This class provides an interface for classes who wish to listener for events within the <strong>MetadataSetManager</strong>. There are events which are fired whenever a set or an element within a set changes. The events are either very broad, of the <i>metadataSetChanged()</i> variaty down to the more explicit <i>elementChanged(Profile change)</i> which actually details what change occured so the GUI etc can update just that element. */
    5555public class MSMListenerAdapter
    56      implements MSMListener {
    57      /** Method that is called whenever an element within a set is changed or modified.
     56    implements MSMListener {
     57    /** Method that is called whenever an element within a set is changed or modified.
     58     * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
     59      */
     60    public void elementChanged(MSMEvent event) {
     61    }
     62    /** Called whenever the metadata value changes in some way, such as the addition of a new value.
    5863      * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    5964      */
    60      public void elementChanged(MSMEvent event) {
    61     }
    62      /** Called whenever the metadata value changes in some way, such as the addition of a new value.
     65    public void metadataChanged(MSMEvent event) {
     66    }
     67    /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets.
    6368      * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    6469      */
    65      public void metadataChanged(MSMEvent event) {
    66     }
    67      /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets.
     70    public void setChanged(MSMEvent event) {
     71    }
     72    /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value.
    6873      * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    6974      */
    70      public void setChanged(MSMEvent event) {
    71      }
    72      /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value.
    73       * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    74       */
    75      public void valueChanged(MSMEvent event) {
    76      }
     75    public void valueChanged(MSMEvent event) {
     76    }
    7777}
  • trunk/gli/src/org/greenstone/gatherer/msm/MSMProfiler.java

    r4293 r4365  
    5757 */
    5858public class MSMProfiler
    59     extends MSMAdapter {
    60     /** The file the profile should be loaded from and saved to. */
    61     private File profile_file = null;
    62     /** A mapping of known profile actions. */
    63     private HashMap3D profiles = null;
    64     static final private String IGNORE = "\nIGNORE\n";
    65     /** The constructor loads the previous profile for this collection if there is one.
     59    extends MSMAdapter {
     60    /** The file the profile should be loaded from and saved to. */
     61    private File profile_file = null;
     62    /** A mapping of known profile actions. */
     63    private HashMap3D profiles = null;
     64    static final private String IGNORE = "\nIGNORE\n";
     65    /** The constructor loads the previous profile for this collection if there is one.
    6666      * @see org.greenstone.gatherer.collection.CollectionManager
    6767      * @see org.greenstone.gatherer.msm.MetadataSetManager
    6868      */
    69     public MSMProfiler() {
    70           this.profile_file = new File(Gatherer.c_man.getCollectionMetadata(), "profile.xml");
    71           this.profiles = new HashMap3D();
    72           // Load an existing profile if there is one.
    73           if(profile_file.exists()) {
    74                 Document document = Utility.parse(profile_file, false);
    75                 if(document != null) {
    76                      load(document);
    77                      document = null;
    78                 }
    79           }
    80     }
    81     /** Adds a new action mapping to the profile. Such a mapping records that for a certain collection (file) and metadata element a specific action must occur. The action is either a renaming to a new metadata element name, also provided, or an instruction to ignore this particular metadata element, is the target is null.
     69    public MSMProfiler() {
     70    this.profile_file = new File(Gatherer.c_man.getCollectionMetadata(), "profile.xml");
     71    this.profiles = new HashMap3D();
     72    // Load an existing profile if there is one.
     73    if(profile_file.exists()) {
     74        Document document = Utility.parse(profile_file, false);
     75        if(document != null) {
     76        load(document);
     77        document = null;
     78        }
     79    }
     80    }
     81    /** Adds a new action mapping to the profile. Such a mapping records that for a certain collection (file) and metadata element a specific action must occur. The action is either a renaming to a new metadata element name, also provided, or an instruction to ignore this particular metadata element, is the target is null.
    8282      * @param collection_file The aboslute path name to the collection where the metadata was sourced, as a <strong>String</strong>.
    8383      * @param source A <strong>String</strong> containing the fully qualified name of the source metadata element.
    8484      * @param target Another <strong>String</strong> which is either the fully qualified name of the target element, or <i>null</i> if this is actually an ignore action addition.
    8585      */
    86     public void addAction(String collection_file, String source, String target) {
    87           if(target == null) {
    88                 target = IGNORE;
    89           }
    90           profiles.put(collection_file, source, target);
    91     }
    92 
    93     public void addSource(String collection_file) {
    94           profiles.put(collection_file, new HashMap());
    95     }
    96 
    97     /** Determine if an action exists for the given collection and source.
     86    public void addAction(String collection_file, String source, String target) {
     87    if(target == null) {
     88        target = IGNORE;
     89    }
     90    profiles.put(collection_file, source, target);
     91    }
     92
     93    public void addSource(String collection_file) {
     94    profiles.put(collection_file, new HashMap());
     95    }
     96
     97    /** Determine if an action exists for the given collection and source.
    9898      * @param collection_file The aboslute path name to the collection where the metadata was sourced, as a <strong>String</strong>.
    9999      * @param source A <strong>String</strong> containing the fully qualified name of the source metadata element.
    100100      * @return <i>true</i> if such an action exists, <i>false</i> otherwise.
    101101      */
    102     public boolean containsAction(String collection_file, String source) {
    103           return profiles.contains(collection_file, source);
    104     }
    105 
    106     public boolean containsSource(String collection_file) {
    107           return profiles.containsKey(collection_file);
    108     }
    109 
    110     /** Destructor to ensure that no memory leaks. */
    111     public void destroy() {
    112           //save();
    113           profiles.clear();
    114           profile_file = null;
    115           profiles = null;
    116     }
    117     /** Method that is called whenever an element within a set is changed or modified, in which case we must modify the action profiles to suit. If an element was removed, remove all action profiles with this element as the target. If an elements name changes, update all matching targets to reflect new name.
     102    public boolean containsAction(String collection_file, String source) {
     103    return profiles.contains(collection_file, source);
     104    }
     105
     106    public boolean containsSource(String collection_file) {
     107    return profiles.containsKey(collection_file);
     108    }
     109
     110    /** Destructor to ensure that no memory leaks. */
     111    public void destroy() {
     112    //save();
     113    profiles.clear();
     114    profile_file = null;
     115    profiles = null;
     116    }
     117    /** Method that is called whenever an element within a set is changed or modified, in which case we must modify the action profiles to suit. If an element was removed, remove all action profiles with this element as the target. If an elements name changes, update all matching targets to reflect new name.
    118118      * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
    119119      */
    120     public void elementChanged(MSMEvent event) {
    121           Gatherer.println("Element changed: " + event);
    122           String new_name = null;
    123           String old_name = event.getValue();
    124           ElementWrapper element = event.getElement();
    125           boolean delete = false;
    126           boolean rename = false;
    127           // First we check if the element has been removed.
    128           if(element == null) {
    129                 delete = true;
    130           }
    131           // Next we determine if its name has changed
    132           else if(!(new_name = element.toString()).equals(old_name)) {
    133                 rename = true;
    134           }
    135           // Perform any action we need to.
    136           if(delete || rename) {
    137                 Iterator iterator_value_one = profiles.values().iterator();
    138                 while(iterator_value_one.hasNext()) {
    139                      HashMap map = (HashMap) iterator_value_one.next();
    140                      Iterator iterator_key_two = map.keySet().iterator();
    141                      while(iterator_key_two.hasNext()) {
    142                           String key_two = (String) iterator_key_two.next();
    143                           String value = (String) map.get(key_two);
    144                           if(value.equals(old_name)) {
    145                                 if(delete) {
    146                                     map.remove(key_two);
    147                                 }
    148                                 else {
    149                                     map.put(key_two, new_name);
    150                                 }
    151                           }
    152                           key_two = null;
    153                           value = null;
    154                      }
    155                      map = null;
    156                      iterator_key_two = null;
    157                 }
    158                 iterator_value_one = null;
    159           }
    160           // Otherwise nothing for us to do (must be an add, or possibly a move if I can ever get round to the editor).
    161           element = null;
    162           old_name = null;
    163           new_name = null;
    164     }
    165     /** Search the profilerer for any previous actions regarding the indicated collection and metadata element.
     120    public void elementChanged(MSMEvent event) {
     121    Gatherer.println("Element changed: " + event);
     122    String new_name = null;
     123    String old_name = event.getValue();
     124    ElementWrapper element = event.getElement();
     125    boolean delete = false;
     126    boolean rename = false;
     127    // First we check if the element has been removed.
     128    if(element == null) {
     129        delete = true;
     130    }
     131    // Next we determine if its name has changed
     132    else if(!(new_name = element.toString()).equals(old_name)) {
     133        rename = true;
     134    }
     135    // Perform any action we need to.
     136    if(delete || rename) {
     137        Iterator iterator_value_one = profiles.values().iterator();
     138        while(iterator_value_one.hasNext()) {
     139        HashMap map = (HashMap) iterator_value_one.next();
     140        Iterator iterator_key_two = map.keySet().iterator();
     141        while(iterator_key_two.hasNext()) {
     142            String key_two = (String) iterator_key_two.next();
     143            String value = (String) map.get(key_two);
     144            if(value.equals(old_name)) {
     145            if(delete) {
     146                map.remove(key_two);
     147            }
     148            else {
     149                map.put(key_two, new_name);
     150            }
     151            }
     152            key_two = null;
     153            value = null;
     154        }
     155        map = null;
     156        iterator_key_two = null;
     157        }
     158        iterator_value_one = null;
     159    }
     160    // Otherwise nothing for us to do (must be an add, or possibly a move if I can ever get round to the editor).
     161    element = null;
     162    old_name = null;
     163    new_name = null;
     164    }
     165    /** Search the profilerer for any previous actions regarding the indicated collection and metadata element.
    166166      * @param collection_file The absolute path name to the collection where the metadata was sourced, as a <strong>String</strong>.
    167167      * @param source A <strong>String</strong> containing the fully qualified name of the source metadata element.
     
    169169      * @see org.greenstone.gatherer.msm.Declarations
    170170      */
    171     public String getAction(String collection_file, String source) {
    172           ///atherer.println("Get action.");
    173           String result = (String) profiles.get(collection_file, source);
    174           if(result.equals(IGNORE)) {
    175                 result = null;
    176           }
    177           return result;
    178     }
    179 
    180     public HashMap getActions(String collection_file) {
    181           return (HashMap) profiles.get(collection_file);
    182     }
     171    public String getAction(String collection_file, String source) {
     172    ///atherer.println("Get action.");
     173    String result = (String) profiles.get(collection_file, source);
     174    if(result.equals(IGNORE)) {
     175        result = null;
     176    }
     177    return result;
     178    }
     179
     180    public HashMap getActions(String collection_file) {
     181    return (HashMap) profiles.get(collection_file);
     182    }
    183183     
    184     public ArrayList getCollections() {
    185           return new ArrayList(profiles.keySet());
    186     }
    187 
    188     public ArrayList getSources(String collection_file) {
    189           ArrayList result = new ArrayList();
    190           HashMap map = (HashMap) profiles.get(collection_file);
    191           if(map != null) {
    192                 result.addAll(map.keySet());
    193           }
    194           return result;
    195     }
    196 
    197     public void removeProfile(String collection_file) {
    198           profiles.remove(collection_file);
    199     }
    200 
    201     public void removeAction(String collection_file, String source) {
    202           HashMap map = (HashMap) profiles.get(collection_file);
    203           if(map != null) {
    204                 map.remove(source);
    205           }
    206     }
    207 
    208     /** Save the profile document. */
    209     public void save() {
    210           // While we're at it save the profiles.
    211           try {
     184    public ArrayList getCollections() {
     185    return new ArrayList(profiles.keySet());
     186    }
     187
     188    public ArrayList getSources(String collection_file) {
     189    ArrayList result = new ArrayList();
     190    HashMap map = (HashMap) profiles.get(collection_file);
     191    if(map != null) {
     192        result.addAll(map.keySet());
     193    }
     194    return result;
     195    }
     196
     197    public void removeProfile(String collection_file) {
     198    profiles.remove(collection_file);
     199    }
     200
     201    public void removeAction(String collection_file, String source) {
     202    HashMap map = (HashMap) profiles.get(collection_file);
     203    if(map != null) {
     204        map.remove(source);
     205    }
     206    }
     207
     208    /** Save the profile document. */
     209    public void save() {
     210    // While we're at it save the profiles.
     211    try {
    212212                // Create backup.
    213                 if(profile_file.exists()) {
    214                      File backup = new File(profile_file.getAbsolutePath() + "~");
    215                      backup.deleteOnExit();
    216                      if(!profile_file.renameTo(backup)) {
    217                           Gatherer.println("Error in MSMProfiler.save(): FileRenameException");
    218                      }
    219                      backup = null;
    220                 }
     213        if(profile_file.exists()) {
     214        File backup = new File(profile_file.getAbsolutePath() + "~");
     215        backup.deleteOnExit();
     216        if(!profile_file.renameTo(backup)) {
     217            Gatherer.println("Error in MSMProfiler.save(): FileRenameException");
     218        }
     219        backup = null;
     220        }
    221221                // Build the profile document.
    222                 StringBuffer buffer = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
    223                 buffer.append("<!DOCTYPE Profiles [\n");
    224                buffer.append("<!ELEMENT Profiles (Action*)>\n");
    225                buffer.append("<!ELEMENT Action (#PCDATA)>\n");
    226                buffer.append("<!ATTLIST Action\n");
    227                 buffer.append("         collection CDATA #REQUIRED\n");
    228                 buffer.append("         source     CDATA #REQUIRED\n");
    229                 buffer.append("         target     CDATA \"\">\n");
     222        StringBuffer buffer = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
     223        buffer.append("<!DOCTYPE Profiles [\n");
     224        buffer.append("<!ELEMENT Profiles (Action*)>\n");
     225        buffer.append("<!ELEMENT Action (#PCDATA)>\n");
     226        buffer.append("<!ATTLIST Action\n");
     227        buffer.append("         collection CDATA #REQUIRED\n");
     228        buffer.append("         source     CDATA #REQUIRED\n");
     229        buffer.append("         target     CDATA \"\">\n");
    230230            buffer.append("]>\n");
    231231            buffer.append("<Profiles>\n");
    232                 Iterator iterator_key_one = profiles.keySet().iterator();
    233                 while(iterator_key_one.hasNext()) {
    234                      String key_one = (String) iterator_key_one.next();
    235                      HashMap map = (HashMap) profiles.get(key_one);
    236                      Iterator iterator_key_two = map.keySet().iterator();
    237                      while(iterator_key_two.hasNext()) {
    238                           String key_two = (String) iterator_key_two.next();
    239                           String value = (String) map.get(key_two);
    240                           buffer.append("  <Action collection=\"");
    241                           buffer.append(key_one);
    242                           buffer.append("\" source=\"");
    243                           buffer.append(key_two);
    244                           buffer.append("\" target=\"");
    245                           buffer.append(value);
    246                           buffer.append("\"> </Action>\n");
    247                           key_two = null;
    248                           value = null;
    249                      }
    250                      key_one = null;
    251                      map = null;
    252                      iterator_key_two = null;
    253                 }
    254                 iterator_key_one = null;
     232        Iterator iterator_key_one = profiles.keySet().iterator();
     233        while(iterator_key_one.hasNext()) {
     234        String key_one = (String) iterator_key_one.next();
     235        HashMap map = (HashMap) profiles.get(key_one);
     236        Iterator iterator_key_two = map.keySet().iterator();
     237        while(iterator_key_two.hasNext()) {
     238            String key_two = (String) iterator_key_two.next();
     239            String value = (String) map.get(key_two);
     240            buffer.append("  <Action collection=\"");
     241            buffer.append(key_one);
     242            buffer.append("\" source=\"");
     243            buffer.append(key_two);
     244            buffer.append("\" target=\"");
     245            buffer.append(value);
     246            buffer.append("\"> </Action>\n");
     247            key_two = null;
     248            value = null;
     249        }
     250        key_one = null;
     251        map = null;
     252        iterator_key_two = null;
     253        }
     254        iterator_key_one = null;
    255255            buffer.append("</Profiles>");
    256                 String buffer_str = buffer.toString();
    257                 buffer = null;
     256        String buffer_str = buffer.toString();
     257        buffer = null;
    258258                // Now write it to file.
    259                 FileOutputStream fos = new FileOutputStream(profile_file);
    260                 OutputStreamWriter osw = new OutputStreamWriter(fos);
    261                 int position = 0;
    262                 int BLOCK_SIZE = 1024;
     259        FileOutputStream fos = new FileOutputStream(profile_file);
     260        OutputStreamWriter osw = new OutputStreamWriter(fos);
     261        int position = 0;
     262        int BLOCK_SIZE = 1024;
    263263                // Write x block sized chunks to file.
    264                 while(position + BLOCK_SIZE < buffer_str.length()) {
    265                      osw.write(buffer_str, position, BLOCK_SIZE);
    266                      position = position + BLOCK_SIZE;
    267                 }
     264        while(position + BLOCK_SIZE < buffer_str.length()) {
     265        osw.write(buffer_str, position, BLOCK_SIZE);
     266        position = position + BLOCK_SIZE;
     267        }
    268268                // Write the remainder of the buffer.
    269                 if(position < buffer_str.length()) {
    270                      osw.write(buffer_str, position, buffer_str.length() - position);
    271                 }
    272                 osw.close();
    273                 fos.close();
    274                 fos = null;
    275                 osw = null;
    276                 buffer_str = null;
    277           }
    278           catch (Exception error) {
    279                 Gatherer.printStackTrace(error);
    280           }
    281     }
    282     /** Reload the mapping information stored in this document.
     269        if(position < buffer_str.length()) {
     270        osw.write(buffer_str, position, buffer_str.length() - position);
     271        }
     272        osw.close();
     273        fos.close();
     274        fos = null;
     275        osw = null;
     276        buffer_str = null;
     277    }
     278    catch (Exception error) {
     279        Gatherer.printStackTrace(error);
     280    }
     281    }
     282    /** Reload the mapping information stored in this document.
    283283      * @param document The <strong>Document</strong> to parse.
    284284      */
    285     private void load(Document document) {
    286           Element root = document.getDocumentElement();
    287           NodeList actions = root.getElementsByTagName("Action");
    288           for(int i = 0; i < actions.getLength(); i++) {
    289                 Element action = (Element) actions.item(i);
    290                 String collection = action.getAttribute("collection");
    291                 String source = action.getAttribute("source");
    292                 String target = action.getAttribute("target");
    293                 if(collection.length() > 0 && source.length() > 0) {
    294                      if(target.length() == 0) {
    295                           target = null;
    296                      }
    297                      profiles.put(collection, source, target);
    298                 }
    299                 action = null;
    300                 collection = null;
    301                 source = null;
    302                 target = null;
    303           }
    304           root = null;
    305           actions = null;
    306     }
     285    private void load(Document document) {
     286    Element root = document.getDocumentElement();
     287    NodeList actions = root.getElementsByTagName("Action");
     288    for(int i = 0; i < actions.getLength(); i++) {
     289        Element action = (Element) actions.item(i);
     290        String collection = action.getAttribute("collection");
     291        String source = action.getAttribute("source");
     292        String target = action.getAttribute("target");
     293        if(collection.length() > 0 && source.length() > 0) {
     294        if(target.length() == 0) {
     295            target = null;
     296        }
     297        profiles.put(collection, source, target);
     298        }
     299        action = null;
     300        collection = null;
     301        source = null;
     302        target = null;
     303    }
     304    root = null;
     305    actions = null;
     306    }
    307307}
    308 
    309 
    310 
    311 
    312 
    313 
  • trunk/gli/src/org/greenstone/gatherer/msm/MSMPrompt.java

    r4293 r4365  
    5454 */
    5555public class MSMPrompt
    56     implements ActionListener {
    57 
    58     private boolean dialog_cancelled = false;
    59     private Dimension screen_size = null;
    60     private int action = Declarations.NO_ACTION;
    61     private JButton add = null;
    62     private JButton cancel = null;
    63     private JButton merge = null;
    64     private JButton rename = null;
    65     private JButton replace = null;
    66     private JButton skip = null;
    67     private JDialog off_screen = null;
    68     private JDialog on_screen = null;
    69     private JDialog dialog = null;
    70     private JLabel current_details_label = null;
    71     private JLabel new_details_label = null;
    72     private JLabel progress_label = null;
    73     private JProgressBar progress = null;
    74     private JTextArea current_details = null;
    75     private JTextArea new_details = null;
    76     private MetadataSetManager manager = null;
    77     private Object result = null;
    78 
    79     final static private Dimension MDE_SIZE = new Dimension(500,200);
    80     final static private Dimension MDS_SIZE = new Dimension(800,400);
    81     final static private Dimension PROGRESS_SIZE = new Dimension(500,80);
    82     final static private Dimension RENAME_LABEL_SIZE = new Dimension(100,25);
    83     final static private Dimension RENAME_SIZE = new Dimension(300,120);
    84     final static private Dimension SELECT_ELEMENT_SIZE = new Dimension(500,280);
    85     final static private Dimension SELECT_LABEL_SIZE = new Dimension(175, 25);
    86     final static private Dimension SELECT_SET_SIZE = new Dimension(600,210);
    87     final static private Dimension SELECT_SIZE = new Dimension(200,200);
    88     final static private int SELECT_LINE_COUNT = 8;
     56    implements ActionListener {
     57
     58    private boolean dialog_cancelled = false;
     59    private Dimension screen_size = null;
     60    private int action = Declarations.NO_ACTION;
     61    private JButton add = null;
     62    private JButton cancel = null;
     63    private JButton merge = null;
     64    private JButton rename = null;
     65    private JButton replace = null;
     66    private JButton skip = null;
     67    private JDialog off_screen = null;
     68    private JDialog on_screen = null;
     69    private JDialog dialog = null;
     70    private JLabel current_details_label = null;
     71    private JLabel new_details_label = null;
     72    private JLabel progress_label = null;
     73    private JProgressBar progress = null;
     74    private JTextArea current_details = null;
     75    private JTextArea new_details = null;
     76    private MetadataSetManager manager = null;
     77    private Object result = null;
     78
     79    final static private Dimension MDE_SIZE = new Dimension(500,200);
     80    final static private Dimension MDS_SIZE = new Dimension(800,400);
     81    final static private Dimension PROGRESS_SIZE = new Dimension(500,80);
     82    final static private Dimension RENAME_LABEL_SIZE = new Dimension(100,25);
     83    final static private Dimension RENAME_SIZE = new Dimension(300,120);
     84    final static private Dimension SELECT_ELEMENT_SIZE = new Dimension(500,280);
     85    final static private Dimension SELECT_LABEL_SIZE = new Dimension(175, 25);
     86    final static private Dimension SELECT_SET_SIZE = new Dimension(600,210);
     87    final static private Dimension SELECT_SIZE = new Dimension(200,200);
     88    final static private int SELECT_LINE_COUNT = 8;
    8989     
    90     public MSMPrompt(MetadataSetManager manager) {
    91           this.manager = manager;
    92           // Create components
    93 
    94           add = new JButton(get("Add"));
    95           add.addActionListener(this);
    96           add.setEnabled(false);
    97           add.setMnemonic(KeyEvent.VK_A);
     90    public MSMPrompt(MetadataSetManager manager) {
     91    this.manager = manager;
     92    // Create components
     93
     94    add = new JButton(get("Add"));
     95    add.addActionListener(this);
     96    add.setEnabled(false);
     97    add.setMnemonic(KeyEvent.VK_A);
    9898         
    9999
    100           cancel = new JButton(get("Cancel"));
    101           cancel.addActionListener(this);
    102           cancel.setEnabled(true);
    103           cancel.setMnemonic(KeyEvent.VK_C);
    104 
    105           current_details = new JTextArea(get("No_Details"));
    106 
    107           current_details_label = new JLabel(get("Current_Details"));
    108 
    109           merge = new JButton(get("Merge"));
    110           merge.addActionListener(this);
    111           merge.setEnabled(true);
    112           merge.setMnemonic(KeyEvent.VK_M);
    113 
    114           new_details = new JTextArea(get("No_Details"));
    115 
    116           new_details_label = new JLabel(get("New_Details"));
    117 
    118           progress = new JProgressBar();
    119           progress.setStringPainted(true);
    120 
    121           progress_label = new JLabel(get("Progress"));
    122 
    123           rename = new JButton(get("Rename"));
    124           rename.addActionListener(this);
    125           rename.setEnabled(false);
    126           rename.setMnemonic(KeyEvent.VK_N);
    127 
    128           replace = new JButton(get("Replace"));
    129           replace.addActionListener(this);
    130           replace.setEnabled(false);
    131           replace.setMnemonic(KeyEvent.VK_R);
    132 
    133           skip = new JButton(get("Skip"));
    134           skip.addActionListener(this);
    135           skip.setEnabled(true);
    136           skip.setMnemonic(KeyEvent.VK_S);
    137 
    138           screen_size = Gatherer.config.screen_size;
    139     }
    140 
    141 /** When called this method produces a nice error message on the screen, advising the user that the add action has failed.
    142       * @param mde_new The <strong>Element</strong> that we were attempting to add.
    143       * @param reason The phrase key for the reason the add failed, as a <strong>String</strong>.
    144       */
    145     public void addFailed(Element mde_new, String reason) {
    146           String args[] = new String[2];
    147           args[0] = mde_new.getAttribute("name");
    148           args[1] = get(reason, null);
    149           JOptionPane.showMessageDialog(off_screen, get("Add_Failed", args), get("Add_Failed_Title", null), JOptionPane.ERROR_MESSAGE);
    150     }
    151 
    152     /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured.
    153       * @param event An <strong>ActionEvent</strong> containing information gatherer when this event occured.
    154       */
    155     public void actionPerformed(ActionEvent event) {
    156           Object source = event.getSource();
    157           action = Declarations.NO_ACTION;
    158           if(source == add) {
    159                 action = Declarations.ADD;
    160           }
    161           else if(source == cancel) {
    162                 action = Declarations.CANCEL;
    163           }
    164           else if(source == merge) {
    165                 action = Declarations.MERGE;
    166           }
    167           else if(source == rename) {
    168                 action = Declarations.RENAME;
    169           }
    170           else if(source == replace) {
    171                 action = Declarations.REPLACE;
    172           }
    173           else if(source == skip) {
    174                 action = Declarations.SKIP;
    175           }
    176           on_screen.hide();
    177     }
    178     /** Method called when the merging process is complete and the progress bar is no longer needed.
    179       */
    180     public void endMerge() {
    181           off_screen.dispose();
    182           off_screen = null;
    183     }
    184     /** Method to indicate that yet another element has been succesfully merged, so that progress bar should reflect this.
    185       */
    186     public void incrementMerge() {
    187           progress.setValue(progress.getValue() + 1);
    188           String percent = ((100 * progress.getValue()) / progress.getMaximum()) + "%";
    189           progress.setString(percent);
    190     }
    191     /** Method to display the metadata element merging prompt, wherein the user determines how the attributes within an element should be merged.
    192       * @param mde_cur The current <strong>Element</strong> we are merging against.
    193       * @param att_cur The attribute <strong>Element</strong> of the current element in question.
    194       * @param mde_new The <strong>Element</strong> which we have choosen to merge in, and whose attributes are being examined.
    195       * @param att_new And the new elements attribute <strong>Element</strong>.
    196       * @return An <i>int</i> specifying what further action should be undertaken, if any.
    197       */
    198     public int mDEPrompt(Element mde_cur, Element att_cur, Element mde_new, Element att_new) {
    199           action = Declarations.NO_ACTION;
    200           // Construction and configuration
    201           JDialog dialog = new JDialog();
    202           dialog.setModal(true);
    203           dialog.setSize(MDE_SIZE);
    204           dialog.setTitle(get("Merge_MDE"));
    205           JPanel content_pane = (JPanel)dialog.getContentPane();
    206 
    207           JLabel cur_name_label = new JLabel(get("Element_Name"));
    208 
    209           JLabel cur_name = new JLabel(MSMUtils.getFullName(mde_cur));
    210           cur_name.setBackground(Color.white);
    211           cur_name.setOpaque(true);
    212 
    213           JLabel cur_att_label = new JLabel(get("Attribute"));
    214 
    215           JLabel cur_att = new JLabel(att_cur.getAttribute("name") +" = " + MSMUtils.getValue(att_cur));
    216           cur_att.setBackground(Color.white);
    217           cur_att.setOpaque(true);
    218 
    219           JLabel new_name_label = new JLabel(cur_name_label.getText());
    220 
    221           JLabel new_name = new JLabel(MSMUtils.getFullName(mde_new));
    222           new_name.setBackground(Color.white);
    223           new_name.setOpaque(true);
    224 
    225           JLabel new_att_label = new JLabel(cur_att_label.getText());
    226 
    227           JLabel new_att = new JLabel(att_new.getAttribute("name") +" = " + MSMUtils.getValue(att_new));
    228           new_att.setBackground(Color.white);
    229           new_att.setOpaque(true);
    230 
    231           replace.setEnabled(true);
    232           skip.setEnabled(true);
    233 
    234           // Layout
    235           JPanel current_pane = new JPanel();
    236           current_pane.setBorder(BorderFactory.createTitledBorder(get("Current_Element")));
    237           current_pane.setLayout(new GridLayout(2,2));
    238           current_pane.add(cur_name_label);
    239           current_pane.add(cur_name);
    240           current_pane.add(cur_att_label);
    241           current_pane.add(cur_att);
    242 
    243           JPanel new_pane = new JPanel();
    244           new_pane.setBorder(BorderFactory.createTitledBorder(get("New_Element")));
    245           new_pane.setLayout(new GridLayout(2,2));
    246           new_pane.add(new_name_label);
    247           new_pane.add(new_name);
    248           new_pane.add(new_att_label);
    249           new_pane.add(new_att);
    250 
    251           JPanel central_pane = new JPanel();
    252           central_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    253           central_pane.setLayout(new GridLayout(2,1));
    254           central_pane.add(current_pane);
    255           central_pane.add(new_pane);
    256 
    257           JPanel button_pane = new JPanel();
    258           button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
    259           button_pane.setLayout(new GridLayout(1,3));
    260           button_pane.add(replace);
    261           button_pane.add(skip);
    262           button_pane.add(cancel);
    263 
    264           content_pane.setLayout(new BorderLayout());
    265           content_pane.add(central_pane, BorderLayout.CENTER);
    266           content_pane.add(button_pane, BorderLayout.SOUTH);
    267 
    268           // Display
    269           dialog.setLocation((screen_size.width - MDE_SIZE.width) / 2, (screen_size.height - MDE_SIZE.height) / 2);
    270           on_screen = dialog;
    271           off_screen.hide();
    272           on_screen.show();
    273           off_screen.show();
    274           on_screen.dispose();
    275 
    276           content_pane = null;
    277           cur_name_label = null;
    278           cur_name = null;
    279           cur_att_label = null;
    280           cur_att = null;
    281           new_name_label = null;
    282           new_name = null;
    283           new_att_label = null;
    284           new_att = null;
    285           current_pane = null;
    286           new_pane = null;
    287           central_pane = null;
    288           button_pane = null;
    289           screen_size = null;
    290           on_screen = null;
    291           dialog = null;
    292 
    293           return action;
    294     }
    295     /** This method displays the metadata data set merging prompt, wherein the user determines how the elements within a set should be merged.
    296       * @param mds_cur The current <strong>MetadataSet</strong> containing our document.
    297       * @param mde_cur The current <strong>Element</strong> we are merging against.
    298       * @param mds_new The <strong>MetadataSet</strong> we are merging in.
    299       * @param mde_new The <strong>Element</strong> which we have choosen to merge in.
    300       * @return An <i>int</i> specifying what further action should be undertaken, if any.
    301       */
    302     public int mDSPrompt(MetadataSet mds_cur, Element mde_cur, MetadataSet mds_new, Element mde_new) {
    303           action = Declarations.NO_ACTION;
    304           // Construction and configuration
    305           JDialog dialog = new JDialog();
    306           dialog.setModal(true);
    307           dialog.setSize(MDS_SIZE);
    308           dialog.setTitle(get("Merge_MDS"));
    309           JPanel content_pane = (JPanel)dialog.getContentPane();
    310           if(mde_cur != null) {
    311                 add.setEnabled(false);
    312                 cancel.setEnabled(true);
    313                 merge.setEnabled(true);
    314                 rename.setEnabled(true);
    315                 replace.setEnabled(true);
    316                 skip.setEnabled(true);
     100    cancel = new JButton(get("Cancel"));
     101    cancel.addActionListener(this);
     102    cancel.setEnabled(true);
     103    cancel.setMnemonic(KeyEvent.VK_C);
     104
     105    current_details = new JTextArea(get("No_Details"));
     106
     107    current_details_label = new JLabel(get("Current_Details"));
     108
     109    merge = new JButton(get("Merge"));
     110    merge.addActionListener(this);
     111    merge.setEnabled(true);
     112    merge.setMnemonic(KeyEvent.VK_M);
     113
     114    new_details = new JTextArea(get("No_Details"));
     115
     116    new_details_label = new JLabel(get("New_Details"));
     117
     118    progress = new JProgressBar();
     119    progress.setStringPainted(true);
     120
     121    progress_label = new JLabel(get("Progress"));
     122
     123    rename = new JButton(get("Rename"));
     124    rename.addActionListener(this);
     125    rename.setEnabled(false);
     126    rename.setMnemonic(KeyEvent.VK_N);
     127
     128    replace = new JButton(get("Replace"));
     129    replace.addActionListener(this);
     130    replace.setEnabled(false);
     131    replace.setMnemonic(KeyEvent.VK_R);
     132
     133    skip = new JButton(get("Skip"));
     134    skip.addActionListener(this);
     135    skip.setEnabled(true);
     136    skip.setMnemonic(KeyEvent.VK_S);
     137
     138    screen_size = Gatherer.config.screen_size;
     139    }
     140
     141    /** When called this method produces a nice error message on the screen, advising the user that the add action has failed.
     142     * @param mde_new The <strong>Element</strong> that we were attempting to add.
     143     * @param reason The phrase key for the reason the add failed, as a <strong>String</strong>.
     144     */
     145    public void addFailed(Element mde_new, String reason) {
     146    String args[] = new String[2];
     147    args[0] = mde_new.getAttribute("name");
     148    args[1] = get(reason, null);
     149    JOptionPane.showMessageDialog(off_screen, get("Add_Failed", args), get("Add_Failed_Title", null), JOptionPane.ERROR_MESSAGE);
     150    }
     151
     152    /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured.
     153     * @param event An <strong>ActionEvent</strong> containing information gatherer when this event occured.
     154     */
     155    public void actionPerformed(ActionEvent event) {
     156    Object source = event.getSource();
     157    action = Declarations.NO_ACTION;
     158    if(source == add) {
     159        action = Declarations.ADD;
     160    }
     161    else if(source == cancel) {
     162        action = Declarations.CANCEL;
     163    }
     164    else if(source == merge) {
     165        action = Declarations.MERGE;
     166    }
     167    else if(source == rename) {
     168        action = Declarations.RENAME;
     169    }
     170    else if(source == replace) {
     171        action = Declarations.REPLACE;
     172    }
     173    else if(source == skip) {
     174        action = Declarations.SKIP;
     175    }
     176    on_screen.hide();
     177    }
     178    /** Method called when the merging process is complete and the progress bar is no longer needed.
     179     */
     180    public void endMerge() {
     181    off_screen.dispose();
     182    off_screen = null;
     183    }
     184    /** Method to indicate that yet another element has been succesfully merged, so that progress bar should reflect this.
     185     */
     186    public void incrementMerge() {
     187    progress.setValue(progress.getValue() + 1);
     188    String percent = ((100 * progress.getValue()) / progress.getMaximum()) + "%";
     189    progress.setString(percent);
     190    }
     191    /** Method to display the metadata element merging prompt, wherein the user determines how the attributes within an element should be merged.
     192     * @param mde_cur The current <strong>Element</strong> we are merging against.
     193     * @param att_cur The attribute <strong>Element</strong> of the current element in question.
     194     * @param mde_new The <strong>Element</strong> which we have choosen to merge in, and whose attributes are being examined.
     195     * @param att_new And the new elements attribute <strong>Element</strong>.
     196     * @return An <i>int</i> specifying what further action should be undertaken, if any.
     197     */
     198    public int mDEPrompt(Element mde_cur, Element att_cur, Element mde_new, Element att_new) {
     199    action = Declarations.NO_ACTION;
     200    // Construction and configuration
     201    JDialog dialog = new JDialog();
     202    dialog.setModal(true);
     203    dialog.setSize(MDE_SIZE);
     204    dialog.setTitle(get("Merge_MDE"));
     205    JPanel content_pane = (JPanel)dialog.getContentPane();
     206
     207    JLabel cur_name_label = new JLabel(get("Element_Name"));
     208
     209    JLabel cur_name = new JLabel(MSMUtils.getFullName(mde_cur));
     210    cur_name.setBackground(Color.white);
     211    cur_name.setOpaque(true);
     212
     213    JLabel cur_att_label = new JLabel(get("Attribute"));
     214
     215    JLabel cur_att = new JLabel(att_cur.getAttribute("name") +" = " + MSMUtils.getValue(att_cur));
     216    cur_att.setBackground(Color.white);
     217    cur_att.setOpaque(true);
     218
     219    JLabel new_name_label = new JLabel(cur_name_label.getText());
     220
     221    JLabel new_name = new JLabel(MSMUtils.getFullName(mde_new));
     222    new_name.setBackground(Color.white);
     223    new_name.setOpaque(true);
     224
     225    JLabel new_att_label = new JLabel(cur_att_label.getText());
     226
     227    JLabel new_att = new JLabel(att_new.getAttribute("name") +" = " + MSMUtils.getValue(att_new));
     228    new_att.setBackground(Color.white);
     229    new_att.setOpaque(true);
     230
     231    replace.setEnabled(true);
     232    skip.setEnabled(true);
     233
     234    // Layout
     235    JPanel current_pane = new JPanel();
     236    current_pane.setBorder(BorderFactory.createTitledBorder(get("Current_Element")));
     237    current_pane.setLayout(new GridLayout(2,2));
     238    current_pane.add(cur_name_label);
     239    current_pane.add(cur_name);
     240    current_pane.add(cur_att_label);
     241    current_pane.add(cur_att);
     242
     243    JPanel new_pane = new JPanel();
     244    new_pane.setBorder(BorderFactory.createTitledBorder(get("New_Element")));
     245    new_pane.setLayout(new GridLayout(2,2));
     246    new_pane.add(new_name_label);
     247    new_pane.add(new_name);
     248    new_pane.add(new_att_label);
     249    new_pane.add(new_att);
     250
     251    JPanel central_pane = new JPanel();
     252    central_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     253    central_pane.setLayout(new GridLayout(2,1));
     254    central_pane.add(current_pane);
     255    central_pane.add(new_pane);
     256
     257    JPanel button_pane = new JPanel();
     258    button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
     259    button_pane.setLayout(new GridLayout(1,3));
     260    button_pane.add(replace);
     261    button_pane.add(skip);
     262    button_pane.add(cancel);
     263
     264    content_pane.setLayout(new BorderLayout());
     265    content_pane.add(central_pane, BorderLayout.CENTER);
     266    content_pane.add(button_pane, BorderLayout.SOUTH);
     267
     268    // Display
     269    dialog.setLocation((screen_size.width - MDE_SIZE.width) / 2, (screen_size.height - MDE_SIZE.height) / 2);
     270    on_screen = dialog;
     271    off_screen.hide();
     272    on_screen.show();
     273    off_screen.show();
     274    on_screen.dispose();
     275
     276    content_pane = null;
     277    cur_name_label = null;
     278    cur_name = null;
     279    cur_att_label = null;
     280    cur_att = null;
     281    new_name_label = null;
     282    new_name = null;
     283    new_att_label = null;
     284    new_att = null;
     285    current_pane = null;
     286    new_pane = null;
     287    central_pane = null;
     288    button_pane = null;
     289    screen_size = null;
     290    on_screen = null;
     291    dialog = null;
     292
     293    return action;
     294    }
     295    /** This method displays the metadata data set merging prompt, wherein the user determines how the elements within a set should be merged.
     296     * @param mds_cur The current <strong>MetadataSet</strong> containing our document.
     297     * @param mde_cur The current <strong>Element</strong> we are merging against.
     298     * @param mds_new The <strong>MetadataSet</strong> we are merging in.
     299     * @param mde_new The <strong>Element</strong> which we have choosen to merge in.
     300     * @return An <i>int</i> specifying what further action should be undertaken, if any.
     301     */
     302    public int mDSPrompt(MetadataSet mds_cur, Element mde_cur, MetadataSet mds_new, Element mde_new) {
     303    action = Declarations.NO_ACTION;
     304    // Construction and configuration
     305    JDialog dialog = new JDialog();
     306    dialog.setModal(true);
     307    dialog.setSize(MDS_SIZE);
     308    dialog.setTitle(get("Merge_MDS"));
     309    JPanel content_pane = (JPanel)dialog.getContentPane();
     310    if(mde_cur != null) {
     311        add.setEnabled(false);
     312        cancel.setEnabled(true);
     313        merge.setEnabled(true);
     314        rename.setEnabled(true);
     315        replace.setEnabled(true);
     316        skip.setEnabled(true);
    317317                // Current details.
    318                 String str_cur[] = MSMUtils.getStructuralDetails(mde_cur);
    319                 String opt_cur[] = MSMUtils.getOptionListDetails(mde_cur);
    320                 String ass_cur[] = MSMUtils.getAssignedValuesDetails(mds_cur, mde_cur);
    321                 String details_cur = get("Structural");
    322                 if(opt_cur != null) {
    323                      details_cur = details_cur + "\n" + get("OptionList", opt_cur);
    324                 }
    325                 if(ass_cur != null) {
    326                      details_cur = details_cur + "\n" + get("AssignedValues", ass_cur);
    327                 }
    328                 current_details.setText(details_cur);
    329                 str_cur = null;
    330                 opt_cur = null;
    331                 ass_cur = null;
    332                 details_cur = null;
    333           }
    334           else {
    335                 add.setEnabled(true);
    336                 cancel.setEnabled(true);
    337                 merge.setEnabled(true);
    338                 rename.setEnabled(false);
    339                 replace.setEnabled(false);
    340                 skip.setEnabled(true);
    341                 current_details.setText(get("No_Element"));
    342           }
    343           // New details.
    344           String str_new[] = MSMUtils.getStructuralDetails(mde_new);
    345           String opt_new[] = MSMUtils.getOptionListDetails(mde_new);
    346           String ass_new[] = MSMUtils.getAssignedValuesDetails(mds_new, mde_new);
    347           String details_new = get("Structural", str_new);
    348           if(opt_new != null) {
    349                 details_new = details_new + "\n" + get("OptionList", opt_new);
    350           }
    351           if(ass_new != null) {
    352                 details_new = details_new + "\n" + get("AssignedValues", ass_new);
    353           }
    354           new_details.setText(details_new);
    355           // Layout
    356           JPanel current_details_pane = new JPanel();
    357           current_details_pane.setLayout(new BorderLayout());
    358           current_details_pane.add(current_details_label, BorderLayout.NORTH);
    359           current_details_pane.add(new JScrollPane(current_details), BorderLayout.CENTER);
    360 
    361           JPanel new_details_pane = new JPanel();
    362           new_details_pane.setLayout(new BorderLayout());
    363           new_details_pane.add(new_details_label, BorderLayout.NORTH);
    364           new_details_pane.add(new JScrollPane(new_details), BorderLayout.CENTER);
    365 
    366           JPanel details_pane = new JPanel();
    367           details_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    368           details_pane.setLayout(new GridLayout(1,2));
    369           details_pane.add(current_details_pane);
    370           details_pane.add(new_details_pane);
    371 
    372           JPanel button_pane = new JPanel();
    373           button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
    374           button_pane.setLayout(new GridLayout(1,6));
    375           button_pane.add(add);
    376           button_pane.add(merge);
    377           button_pane.add(rename);
    378           button_pane.add(replace);
    379           button_pane.add(skip);
    380           button_pane.add(cancel);
     318        String str_cur[] = MSMUtils.getStructuralDetails(mde_cur);
     319        String opt_cur[] = MSMUtils.getOptionListDetails(mde_cur);
     320        String ass_cur[] = MSMUtils.getAssignedValuesDetails(mds_cur, mde_cur);
     321        String details_cur = get("Structural");
     322        if(opt_cur != null) {
     323        details_cur = details_cur + "\n" + get("OptionList", opt_cur);
     324        }
     325        if(ass_cur != null) {
     326        details_cur = details_cur + "\n" + get("AssignedValues", ass_cur);
     327        }
     328        current_details.setText(details_cur);
     329        str_cur = null;
     330        opt_cur = null;
     331        ass_cur = null;
     332        details_cur = null;
     333    }
     334    else {
     335        add.setEnabled(true);
     336        cancel.setEnabled(true);
     337        merge.setEnabled(true);
     338        rename.setEnabled(false);
     339        replace.setEnabled(false);
     340        skip.setEnabled(true);
     341        current_details.setText(get("No_Element"));
     342    }
     343    // New details.
     344    String str_new[] = MSMUtils.getStructuralDetails(mde_new);
     345    String opt_new[] = MSMUtils.getOptionListDetails(mde_new);
     346    String ass_new[] = MSMUtils.getAssignedValuesDetails(mds_new, mde_new);
     347    String details_new = get("Structural", str_new);
     348    if(opt_new != null) {
     349        details_new = details_new + "\n" + get("OptionList", opt_new);
     350    }
     351    if(ass_new != null) {
     352        details_new = details_new + "\n" + get("AssignedValues", ass_new);
     353    }
     354    new_details.setText(details_new);
     355    // Layout
     356    JPanel current_details_pane = new JPanel();
     357    current_details_pane.setLayout(new BorderLayout());
     358    current_details_pane.add(current_details_label, BorderLayout.NORTH);
     359    current_details_pane.add(new JScrollPane(current_details), BorderLayout.CENTER);
     360
     361    JPanel new_details_pane = new JPanel();
     362    new_details_pane.setLayout(new BorderLayout());
     363    new_details_pane.add(new_details_label, BorderLayout.NORTH);
     364    new_details_pane.add(new JScrollPane(new_details), BorderLayout.CENTER);
     365
     366    JPanel details_pane = new JPanel();
     367    details_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     368    details_pane.setLayout(new GridLayout(1,2));
     369    details_pane.add(current_details_pane);
     370    details_pane.add(new_details_pane);
     371
     372    JPanel button_pane = new JPanel();
     373    button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
     374    button_pane.setLayout(new GridLayout(1,6));
     375    button_pane.add(add);
     376    button_pane.add(merge);
     377    button_pane.add(rename);
     378    button_pane.add(replace);
     379    button_pane.add(skip);
     380    button_pane.add(cancel);
    381381         
    382           JPanel control_pane = new JPanel();
    383           control_pane.setLayout(new BorderLayout());
    384           control_pane.add(details_pane, BorderLayout.CENTER);
    385           control_pane.add(button_pane, BorderLayout.SOUTH);
    386 
    387           JPanel progress_pane = new JPanel();
    388           progress_pane.setBorder(BorderFactory.createEmptyBorder(5,5,0,5));
    389           progress_pane.setLayout(new BorderLayout());
    390           progress_pane.add(progress_label, BorderLayout.NORTH);
    391           progress_pane.add(progress, BorderLayout.CENTER);
    392 
    393           content_pane.setLayout(new BorderLayout());
    394           content_pane.add(progress_pane, BorderLayout.NORTH);
    395           content_pane.add(control_pane, BorderLayout.CENTER);
    396 
    397           // Display
    398           dialog.setLocation((screen_size.width - MDS_SIZE.width) / 2, (screen_size.height - MDS_SIZE.height) / 2);
    399           on_screen = dialog;
    400           off_screen.hide();
    401           on_screen.show(); // Blocks until hidden.
    402           off_screen.show();
    403           on_screen.dispose();
    404           on_screen = null;
    405 
    406           content_pane = null;
    407           str_new = null;
    408           opt_new = null;
    409           ass_new = null;
    410           details_new = null;
    411           current_details_pane = null;
    412           new_details_pane = null;
    413           details_pane = null;
    414           button_pane = null;
    415           control_pane = null;
    416           progress_pane = null;
    417           dialog = null;
    418 
    419           if(mde_cur == null && action == Declarations.MERGE) {
    420                 action = Declarations.FORCE_MERGE;
    421           }
    422           return action;
    423     }
    424     /** This method creates an initial JDialog which simply shows the MSMs progress towards merging the metadata sets.
    425       * @param element_count An <i>int</i> specifying the total number of elements to be merged.
    426       */
    427      public void startMerge(int element_count) {
    428           action = Declarations.NO_ACTION;
    429           JDialog dialog = new JDialog();
    430           dialog.setModal(false);
    431           dialog.setSize(PROGRESS_SIZE);
    432           dialog.setTitle(get("Merge_Progress"));
    433           JPanel content_pane = (JPanel)dialog.getContentPane();
    434           progress.setMaximum(element_count);
    435           progress.setMinimum(0);
    436           progress.setString("0%");
    437           progress.setValue(0);
    438           // Layout.
    439           content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    440           content_pane.setLayout(new BorderLayout());
    441           content_pane.add(progress_label, BorderLayout.NORTH);
    442           content_pane.add(progress, BorderLayout.CENTER);
    443           // Display.
    444           dialog.setLocation((screen_size.width - PROGRESS_SIZE.width) / 2, (screen_size.height - PROGRESS_SIZE.height) / 2);
    445           off_screen = dialog;
    446           dialog.show();
    447           content_pane = null;
    448           dialog = null;
    449     }
    450     /** This method creates a prompt for renaming an Element.
    451       * @param mde_new The <strong>Element</strong> we wish to rename.
    452       * @return The new name as a <strong>String</strong>, <i>null</i> if cancelled.
    453       */
    454     public String rename(Element mde_new) {
    455           action = Declarations.NO_ACTION;
    456           // Create
    457           JDialog dialog = new JDialog();
    458           dialog.setModal(true);
    459           dialog.setSize(RENAME_SIZE);
    460           dialog.setTitle(get("Rename"));
    461 
    462           JLabel old_name_label = new JLabel(get("Old_Name"));
    463           old_name_label.setPreferredSize(RENAME_LABEL_SIZE);
    464 
    465           JLabel old_name = new JLabel(mde_new.getAttribute("name"));
    466           old_name.setBackground(Color.white);
    467           old_name.setOpaque(true);
    468 
    469           JLabel new_name_label = new JLabel(get("New_Name"));
    470           new_name_label.setPreferredSize(RENAME_LABEL_SIZE);
     382    JPanel control_pane = new JPanel();
     383    control_pane.setLayout(new BorderLayout());
     384    control_pane.add(details_pane, BorderLayout.CENTER);
     385    control_pane.add(button_pane, BorderLayout.SOUTH);
     386
     387    JPanel progress_pane = new JPanel();
     388    progress_pane.setBorder(BorderFactory.createEmptyBorder(5,5,0,5));
     389    progress_pane.setLayout(new BorderLayout());
     390    progress_pane.add(progress_label, BorderLayout.NORTH);
     391    progress_pane.add(progress, BorderLayout.CENTER);
     392
     393    content_pane.setLayout(new BorderLayout());
     394    content_pane.add(progress_pane, BorderLayout.NORTH);
     395    content_pane.add(control_pane, BorderLayout.CENTER);
     396
     397    // Display
     398    dialog.setLocation((screen_size.width - MDS_SIZE.width) / 2, (screen_size.height - MDS_SIZE.height) / 2);
     399    on_screen = dialog;
     400    off_screen.hide();
     401    on_screen.show(); // Blocks until hidden.
     402    off_screen.show();
     403    on_screen.dispose();
     404    on_screen = null;
     405
     406    content_pane = null;
     407    str_new = null;
     408    opt_new = null;
     409    ass_new = null;
     410    details_new = null;
     411    current_details_pane = null;
     412    new_details_pane = null;
     413    details_pane = null;
     414    button_pane = null;
     415    control_pane = null;
     416    progress_pane = null;
     417    dialog = null;
     418
     419    if(mde_cur == null && action == Declarations.MERGE) {
     420        action = Declarations.FORCE_MERGE;
     421    }
     422    return action;
     423    }
     424    /** This method creates an initial JDialog which simply shows the MSMs progress towards merging the metadata sets.
     425     * @param element_count An <i>int</i> specifying the total number of elements to be merged.
     426     */
     427   public void startMerge(int element_count) {
     428    action = Declarations.NO_ACTION;
     429    JDialog dialog = new JDialog();
     430    dialog.setModal(false);
     431    dialog.setSize(PROGRESS_SIZE);
     432    dialog.setTitle(get("Merge_Progress"));
     433    JPanel content_pane = (JPanel)dialog.getContentPane();
     434    progress.setMaximum(element_count);
     435    progress.setMinimum(0);
     436    progress.setString("0%");
     437    progress.setValue(0);
     438    // Layout.
     439    content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     440    content_pane.setLayout(new BorderLayout());
     441    content_pane.add(progress_label, BorderLayout.NORTH);
     442    content_pane.add(progress, BorderLayout.CENTER);
     443    // Display.
     444    dialog.setLocation((screen_size.width - PROGRESS_SIZE.width) / 2, (screen_size.height - PROGRESS_SIZE.height) / 2);
     445    off_screen = dialog;
     446    dialog.show();
     447    content_pane = null;
     448    dialog = null;
     449    }
     450    /** This method creates a prompt for renaming an Element.
     451     * @param mde_new The <strong>Element</strong> we wish to rename.
     452     * @return The new name as a <strong>String</strong>, <i>null</i> if cancelled.
     453     */
     454    public String rename(Element mde_new) {
     455    action = Declarations.NO_ACTION;
     456    // Create
     457    JDialog dialog = new JDialog();
     458    dialog.setModal(true);
     459    dialog.setSize(RENAME_SIZE);
     460    dialog.setTitle(get("Rename"));
     461
     462    JLabel old_name_label = new JLabel(get("Old_Name"));
     463    old_name_label.setPreferredSize(RENAME_LABEL_SIZE);
     464
     465    JLabel old_name = new JLabel(mde_new.getAttribute("name"));
     466    old_name.setBackground(Color.white);
     467    old_name.setOpaque(true);
     468
     469    JLabel new_name_label = new JLabel(get("New_Name"));
     470    new_name_label.setPreferredSize(RENAME_LABEL_SIZE);
    471471         
    472           JTextField new_name = new JTextField("");
    473           new_name.grabFocus();
    474 
    475           JButton ok = new JButton(get("General.OK"));
    476           ok.addActionListener(this);
    477           ok.setMnemonic(KeyEvent.VK_O);
    478 
    479           JPanel content_pane = (JPanel) dialog.getContentPane();
    480 
    481           // Layout
    482           JPanel old_name_pane = new JPanel();
    483           old_name_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    484           old_name_pane.setLayout(new BorderLayout());
    485           old_name_pane.add(old_name_label, BorderLayout.WEST);
    486           old_name_pane.add(old_name, BorderLayout.CENTER);
    487 
    488           JPanel new_name_pane = new JPanel();
    489           new_name_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
    490           new_name_pane.setLayout(new BorderLayout());
    491           new_name_pane.add(new_name_label, BorderLayout.WEST);
    492           new_name_pane.add(new_name, BorderLayout.CENTER);       
    493 
    494           JPanel control_pane = new JPanel();
    495           control_pane.setLayout(new GridLayout(2,1));
    496           control_pane.add(old_name_pane);
    497           control_pane.add(new_name_pane);
    498 
    499           JPanel button_pane = new JPanel();
    500           button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
    501           button_pane.setLayout(new GridLayout(1,2));
    502           button_pane.add(ok);
    503           button_pane.add(cancel);
    504 
    505           content_pane.setLayout(new BorderLayout());
    506           content_pane.add(control_pane, BorderLayout.CENTER);
    507           content_pane.add(button_pane, BorderLayout.SOUTH);
    508           // Display
    509           dialog.setLocation((screen_size.width - RENAME_SIZE.width) / 2, (screen_size.height - RENAME_SIZE.height) / 2);
    510           on_screen = dialog;
    511           off_screen.hide();
    512           on_screen.show();
    513           off_screen.show();
    514           on_screen.dispose();
    515           on_screen = null;       
    516 
    517           button_pane = null;   
    518           control_pane = null; 
    519           new_name_pane = null;
    520           old_name_pane = null;
    521           content_pane = null;
    522           ok.removeActionListener(this);
    523           ok = null;   
    524           new_name = null; 
    525           new_name_label = null;   
    526           old_name = null; 
    527           old_name_label = null;   
    528           dialog = null;   
    529 
    530           if(action == Declarations.CANCEL) {
    531                 return null;
    532           }
    533           action = Declarations.NO_ACTION;
    534           dialog = null;
    535           return new_name.getText();
    536     }
    537 
    538     /** When called this method produces a nice error message on the screen, advising the user that the remove action has failed.
    539       * @param mde_cur The <strong>Element</strong> that we are attempting to remove.
    540       * @param reason The phrase key for the reason the rename failed, also as a <strong>String</strong>.
    541       */
    542     public void removeFailed(Element mde_cur, String reason) {
    543           String args[] = new String[2];
    544           args[0] = mde_cur.getAttribute("name");
    545           args[1] = get(reason, null);
    546           JOptionPane.showMessageDialog(off_screen, get("Remove_Failed", args), get("Remove_Failed_Title", null), JOptionPane.ERROR_MESSAGE);
    547     }
    548 
    549     /** When called this method produces a nice error message on the screen, advising the user that the rename action has failed.
    550       * @param mde_new The <strong>Element</strong> that we are attempting to add.
    551       * @param new_name The name that we attempted to add it under, as a <strong>String</strong>.
    552       * @param reason The phrase key for the reason the rename failed, also as a <strong>String</strong>.
    553       */
    554     public void renameFailed(Element mde_new, String new_name, String reason) {
    555           String args[] = new String[3];
    556           args[0] = mde_new.getAttribute("name");
    557           args[1] = new_name;
    558           args[2] = get(reason, null);
    559           JOptionPane.showMessageDialog(off_screen, get("Rename_Failed", args), get("Rename_Failed_Title", null), JOptionPane.ERROR_MESSAGE);
    560     }
    561 
    562     /** Method to display a prompt asking the user to select a specific element to merge with.
    563       * @param mds_cur The current <strong>MetadataSet</strong>.
    564       * @return The selected <strong>Element</strong> or <i>null</i> if the user cancels the action.
    565       */
    566     public Element selectElement(MetadataSet mds_cur) {
    567           action = Declarations.NO_ACTION;
    568           // Create
    569           JDialog dialog = new JDialog();
    570           dialog.setModal(true);
    571           dialog.setSize(SELECT_SIZE);
    572           dialog.setTitle(get("Select"));
     472    JTextField new_name = new JTextField("");
     473    new_name.grabFocus();
     474
     475    JButton ok = new JButton(get("General.OK"));
     476    ok.addActionListener(this);
     477    ok.setMnemonic(KeyEvent.VK_O);
     478
     479    JPanel content_pane = (JPanel) dialog.getContentPane();
     480
     481    // Layout
     482    JPanel old_name_pane = new JPanel();
     483    old_name_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     484    old_name_pane.setLayout(new BorderLayout());
     485    old_name_pane.add(old_name_label, BorderLayout.WEST);
     486    old_name_pane.add(old_name, BorderLayout.CENTER);
     487
     488    JPanel new_name_pane = new JPanel();
     489    new_name_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
     490    new_name_pane.setLayout(new BorderLayout());
     491    new_name_pane.add(new_name_label, BorderLayout.WEST);
     492    new_name_pane.add(new_name, BorderLayout.CENTER);         
     493
     494    JPanel control_pane = new JPanel();
     495    control_pane.setLayout(new GridLayout(2,1));
     496    control_pane.add(old_name_pane);
     497    control_pane.add(new_name_pane);
     498
     499    JPanel button_pane = new JPanel();
     500    button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
     501    button_pane.setLayout(new GridLayout(1,2));
     502    button_pane.add(ok);
     503    button_pane.add(cancel);
     504
     505    content_pane.setLayout(new BorderLayout());
     506    content_pane.add(control_pane, BorderLayout.CENTER);
     507    content_pane.add(button_pane, BorderLayout.SOUTH);
     508    // Display
     509    dialog.setLocation((screen_size.width - RENAME_SIZE.width) / 2, (screen_size.height - RENAME_SIZE.height) / 2);
     510    on_screen = dialog;
     511    off_screen.hide();
     512    on_screen.show();
     513    off_screen.show();
     514    on_screen.dispose();
     515    on_screen = null;         
     516
     517    button_pane = null;
     518    control_pane = null;   
     519    new_name_pane = null;   
     520    old_name_pane = null;   
     521    content_pane = null;
     522    ok.removeActionListener(this);
     523    ok = null; 
     524    new_name = null;   
     525    new_name_label = null; 
     526    old_name = null;   
     527    old_name_label = null; 
     528    dialog = null; 
     529
     530    if(action == Declarations.CANCEL) {
     531        return null;
     532    }
     533    action = Declarations.NO_ACTION;
     534    dialog = null;
     535    return new_name.getText();
     536    }
     537
     538    /** When called this method produces a nice error message on the screen, advising the user that the remove action has failed.
     539     * @param mde_cur The <strong>Element</strong> that we are attempting to remove.
     540     * @param reason The phrase key for the reason the rename failed, also as a <strong>String</strong>.
     541     */
     542    public void removeFailed(Element mde_cur, String reason) {
     543    String args[] = new String[2];
     544    args[0] = mde_cur.getAttribute("name");
     545    args[1] = get(reason, null);
     546    JOptionPane.showMessageDialog(off_screen, get("Remove_Failed", args), get("Remove_Failed_Title", null), JOptionPane.ERROR_MESSAGE);
     547    }
     548
     549    /** When called this method produces a nice error message on the screen, advising the user that the rename action has failed.
     550     * @param mde_new The <strong>Element</strong> that we are attempting to add.
     551     * @param new_name The name that we attempted to add it under, as a <strong>String</strong>.
     552     * @param reason The phrase key for the reason the rename failed, also as a <strong>String</strong>.
     553     */
     554    public void renameFailed(Element mde_new, String new_name, String reason) {
     555    String args[] = new String[3];
     556    args[0] = mde_new.getAttribute("name");
     557    args[1] = new_name;
     558    args[2] = get(reason, null);
     559    JOptionPane.showMessageDialog(off_screen, get("Rename_Failed", args), get("Rename_Failed_Title", null), JOptionPane.ERROR_MESSAGE);
     560    }
     561
     562    /** Method to display a prompt asking the user to select a specific element to merge with.
     563     * @param mds_cur The current <strong>MetadataSet</strong>.
     564     * @return The selected <strong>Element</strong> or <i>null</i> if the user cancels the action.
     565     */
     566    public Element selectElement(MetadataSet mds_cur) {
     567    action = Declarations.NO_ACTION;
     568    // Create
     569    JDialog dialog = new JDialog();
     570    dialog.setModal(true);
     571    dialog.setSize(SELECT_SIZE);
     572    dialog.setTitle(get("Select"));
    573573         
    574           JButton ok = new JButton(get("General.OK"));
    575           ok.addActionListener(this);
    576           ok.setMnemonic(KeyEvent.VK_O);
    577           ok.setEnabled(false);
    578 
    579           Node elements_raw[] = ArrayTools.nodeListToNodeArray(mds_cur.getElements());
    580           ElementWrapper elements[] = new ElementWrapper[elements_raw.length];
    581           for(int i = 0; i < elements_raw.length; i++) {
    582                 elements[i] = new ElementWrapper((Element)elements_raw[i]);
    583           }
    584           JList list = new JList(elements);
    585           ElementListListener element_list_listener = new ElementListListener(list, ok);
    586           list.addListSelectionListener(element_list_listener);
    587           JPanel content_pane = (JPanel) dialog.getContentPane();
    588           // Layout
    589           JPanel list_pane = new JPanel();
    590           list_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    591           list_pane.setLayout(new BorderLayout());
    592           list_pane.add(new JScrollPane(list), BorderLayout.CENTER);
     574    JButton ok = new JButton(get("General.OK"));
     575    ok.addActionListener(this);
     576    ok.setMnemonic(KeyEvent.VK_O);
     577    ok.setEnabled(false);
     578
     579    Node elements_raw[] = ArrayTools.nodeListToNodeArray(mds_cur.getElements());
     580    ElementWrapper elements[] = new ElementWrapper[elements_raw.length];
     581    for(int i = 0; i < elements_raw.length; i++) {
     582        elements[i] = new ElementWrapper((Element)elements_raw[i]);
     583    }
     584    JList list = new JList(elements);
     585    ElementListListener element_list_listener = new ElementListListener(list, ok);
     586    list.addListSelectionListener(element_list_listener);
     587    JPanel content_pane = (JPanel) dialog.getContentPane();
     588    // Layout
     589    JPanel list_pane = new JPanel();
     590    list_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     591    list_pane.setLayout(new BorderLayout());
     592    list_pane.add(new JScrollPane(list), BorderLayout.CENTER);
    593593         
    594           JPanel button_pane = new JPanel();
    595           button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
    596           button_pane.setLayout(new GridLayout(1,2));
    597           button_pane.add(ok);
    598           button_pane.add(cancel);
    599 
    600           content_pane.setLayout(new BorderLayout());
    601           content_pane.add(list_pane, BorderLayout.CENTER);
    602           content_pane.add(button_pane, BorderLayout.SOUTH);
    603           // Display
    604           dialog.setLocation((screen_size.width - SELECT_SIZE.width) / 2, (screen_size.height - SELECT_SIZE.height) / 2);
    605           on_screen = dialog;
    606           off_screen.hide();
    607           on_screen.show();
    608           off_screen.show();
    609           on_screen.dispose();
    610           on_screen = null;
    611           // Deallocate stuff since JDK1.4 won't.
    612           ok.removeActionListener(this);
    613           ok = null;
    614           elements_raw = null;
    615           elements = null;
    616           list.removeListSelectionListener(element_list_listener);
    617           list = null;
    618           element_list_listener = null;
    619           content_pane = null;
    620           list_pane = null;
    621           button_pane = null;
    622           dialog = null;
    623           // Return selected element.
    624           if(action == Declarations.CANCEL) {
    625                 return null;
    626           }
    627           return ((ElementWrapper)list.getSelectedValue()).getElement();
    628     }
    629 
    630     /** Prompts the user to choose how to import a metadata element. Gives the option of renaming the element to a certain value of a certain set, or adding to a selected set.
    631       * @param name The name of the original metadata as a String.
    632       * @param set The set previously choosen as the default set for this metadata (which it doesn't match somehow).
    633       * @return An ElementWrapper around the element that it has been matched to.
    634       * @see org.greenstone.gatherer.msm.MSMPrompt.MSMDialog
    635       * @see org.greenstone.gatherer.util.MED
    636       */
    637     public ElementWrapper selectElement(String name) {
    638           dialog_cancelled = false;
    639           result = null;
    640           String args[] = new String[1];
    641           args[0] = name;
    642           // Create
    643           MSMDialog dialog = new MSMDialog();
    644           dialog.setModal(true);
    645           dialog.setSize(SELECT_ELEMENT_SIZE);
    646           dialog.setTitle(get("Select_Element_Title"));
    647           JPanel content_pane = (JPanel) dialog.getContentPane();
    648           JPanel control_pane = new JPanel();
    649           JTextArea instructions = new JTextArea(get("Select_Element_Instructions", args));
    650           instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    651           instructions.setEditable(false);
    652           instructions.setLineWrap(true);
    653           instructions.setRows(SELECT_LINE_COUNT);
    654           instructions.setWrapStyleWord(true);
    655           JPanel original_pane = new JPanel();
    656           JLabel original_label = new JLabel(get("Select_Element_Original"));
    657           original_label.setPreferredSize(SELECT_LABEL_SIZE);
    658           JTextField original = new JTextField(name);
    659           original.setEditable(false);
    660           JPanel element_pane = new JPanel();
    661           JLabel element_label = new JLabel(get("Select_Element_Element"));
    662           element_label.setPreferredSize(SELECT_LABEL_SIZE);
    663           JComboBox element = new JComboBox();
    664           element.setBackground(Color.white);
    665           JButton add_button = new JButton(get("Select_Element_Add"));
    666           add_button.setEnabled(false);
    667           add_button.setMnemonic(KeyEvent.VK_A);
    668           JButton cancel_button = new JButton(get("General.Cancel"));
    669           cancel_button.setMnemonic(KeyEvent.VK_C);
    670           JButton merge_button = new JButton(get("Select_Element_Merge"));
    671           merge_button.setEnabled(false);
    672           merge_button.setMnemonic(KeyEvent.VK_M);
    673           JButton ignore_button = new JButton(get("Select_Element_Ignore"));
    674           ignore_button.setMnemonic(KeyEvent.VK_I);
    675           JPanel set_pane = new JPanel();
    676           JLabel set_label = new JLabel(get("Select_Element_Set"));
    677           set_label.setPreferredSize(SELECT_LABEL_SIZE);
    678           JComboBox set = new JComboBox(manager.getSets(false)); // Don't include the greenstone metadata set.
    679           set.setBackground(Color.white);
    680           JPanel button_pane = new JPanel();
    681           // Connect
    682           AddListener add_listener = new AddListener(dialog, name, set);
    683           CancelListener cancel_listener = new CancelListener(dialog);
    684           IgnoreListener ignore_listener = new IgnoreListener(dialog);
    685           MergeListener merge_listener = new MergeListener(dialog, element);
    686           SetListener set_listener = new SetListener(set, element, name, add_button, merge_button);
    687 
    688           add_button.addActionListener(add_listener);
    689           cancel_button.addActionListener(cancel_listener);
    690           merge_button.addActionListener(merge_listener);
    691           ignore_button.addActionListener(ignore_listener);
    692           set.addActionListener(set_listener);
    693 
    694           // Init controls
    695           if(set.getItemCount() > 0) {
     594    JPanel button_pane = new JPanel();
     595    button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
     596    button_pane.setLayout(new GridLayout(1,2));
     597    button_pane.add(ok);
     598    button_pane.add(cancel);
     599
     600    content_pane.setLayout(new BorderLayout());
     601    content_pane.add(list_pane, BorderLayout.CENTER);
     602    content_pane.add(button_pane, BorderLayout.SOUTH);
     603    // Display
     604    dialog.setLocation((screen_size.width - SELECT_SIZE.width) / 2, (screen_size.height - SELECT_SIZE.height) / 2);
     605    on_screen = dialog;
     606    off_screen.hide();
     607    on_screen.show();
     608    off_screen.show();
     609    on_screen.dispose();
     610    on_screen = null;   
     611    // Deallocate stuff since JDK1.4 won't.
     612    ok.removeActionListener(this);
     613    ok = null;
     614    elements_raw = null;
     615    elements = null;
     616    list.removeListSelectionListener(element_list_listener);
     617    list = null;
     618    element_list_listener = null;
     619    content_pane = null;
     620    list_pane = null;
     621    button_pane = null;
     622    dialog = null;
     623    // Return selected element.
     624    if(action == Declarations.CANCEL) {
     625        return null;
     626    }
     627    return ((ElementWrapper)list.getSelectedValue()).getElement();
     628    }
     629
     630    /** Prompts the user to choose how to import a metadata element. Gives the option of renaming the element to a certain value of a certain set, or adding to a selected set.
     631     * @param name The name of the original metadata as a String.
     632     * @param set The set previously choosen as the default set for this metadata (which it doesn't match somehow).
     633     * @return An ElementWrapper around the element that it has been matched to.
     634     * @see org.greenstone.gatherer.msm.MSMPrompt.MSMDialog
     635     * @see org.greenstone.gatherer.util.MED
     636     */
     637    public ElementWrapper selectElement(String name) {
     638    dialog_cancelled = false;
     639    result = null;
     640    String args[] = new String[1];
     641    args[0] = name;
     642    // Create
     643    MSMDialog dialog = new MSMDialog();
     644    dialog.setModal(true);
     645    dialog.setSize(SELECT_ELEMENT_SIZE);
     646    dialog.setTitle(get("Select_Element_Title"));
     647    JPanel content_pane = (JPanel) dialog.getContentPane();
     648    JPanel control_pane = new JPanel();
     649    JTextArea instructions = new JTextArea(get("Select_Element_Instructions", args));
     650    instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     651    instructions.setEditable(false);
     652    instructions.setLineWrap(true);
     653    instructions.setRows(SELECT_LINE_COUNT);
     654    instructions.setWrapStyleWord(true);
     655    JPanel original_pane = new JPanel();
     656    JLabel original_label = new JLabel(get("Select_Element_Original"));
     657    original_label.setPreferredSize(SELECT_LABEL_SIZE);
     658    JTextField original = new JTextField(name);
     659    original.setEditable(false);
     660    JPanel element_pane = new JPanel();
     661    JLabel element_label = new JLabel(get("Select_Element_Element"));
     662    element_label.setPreferredSize(SELECT_LABEL_SIZE);
     663    JComboBox element = new JComboBox();
     664    element.setBackground(Color.white);
     665    JButton add_button = new JButton(get("Select_Element_Add"));
     666    add_button.setEnabled(false);
     667    add_button.setMnemonic(KeyEvent.VK_A);
     668    JButton cancel_button = new JButton(get("General.Cancel"));
     669    cancel_button.setMnemonic(KeyEvent.VK_C);
     670    JButton merge_button = new JButton(get("Select_Element_Merge"));
     671    merge_button.setEnabled(false);
     672    merge_button.setMnemonic(KeyEvent.VK_M);
     673    JButton ignore_button = new JButton(get("Select_Element_Ignore"));
     674    ignore_button.setMnemonic(KeyEvent.VK_I);
     675    JPanel set_pane = new JPanel();
     676    JLabel set_label = new JLabel(get("Select_Element_Set"));
     677    set_label.setPreferredSize(SELECT_LABEL_SIZE);
     678    JComboBox set = new JComboBox(manager.getSets(false)); // Don't include the greenstone metadata set.
     679    set.setBackground(Color.white);
     680    JPanel button_pane = new JPanel();
     681    // Connect
     682    AddListener add_listener = new AddListener(dialog, name, set);
     683    CancelListener cancel_listener = new CancelListener(dialog);
     684    IgnoreListener ignore_listener = new IgnoreListener(dialog);
     685    MergeListener merge_listener = new MergeListener(dialog, element);
     686    SetListener set_listener = new SetListener(set, element, name, add_button, merge_button);
     687
     688    add_button.addActionListener(add_listener);
     689    cancel_button.addActionListener(cancel_listener);
     690    merge_button.addActionListener(merge_listener);
     691    ignore_button.addActionListener(ignore_listener);
     692    set.addActionListener(set_listener);
     693
     694    // Init controls
     695    if(set.getItemCount() > 0) {
    696696                // We now try to determine the existing metadata element that is the closest match to the one we are trying to merge.
    697                 MetadataSet closest_set = null;
    698                 ElementWrapper closest_element = null;
    699                 int med = -1;
    700                 for(int i = 0; i < set.getItemCount(); i++) {
    701                      MetadataSet mds = (MetadataSet) set.getItemAt(i);
    702                      for(int j = 0; j < mds.size(); j++) {
    703                           ElementWrapper mde = new ElementWrapper(mds.getElement(j));
    704                           int new_med = MED.LD(name, mde.toString());
    705                           if(med == -1 || new_med < med) {
    706                                 med = new_med;
    707                                 closest_element = mde;
    708                                 closest_set = mds;
    709                           }
    710                           mde = null;
    711                      }
    712                      mds = null;
    713                 }
    714                 if(closest_set != null && closest_element != null) {
    715                      set.setSelectedItem(closest_set);
    716                      element.setSelectedItem(closest_element);
    717                      set_listener.actionPerformed();
    718                 }
    719                 else {
    720                      set_listener.actionPerformed();
    721                 }
    722                 closest_set = null;
    723                 closest_element = null;
    724           }
    725           merge_button.setEnabled(element.getItemCount() > 0);
    726           // Layout
    727           original_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
    728           original_pane.setLayout(new BorderLayout());
    729           original_pane.add(original_label, BorderLayout.WEST);
    730           original_pane.add(original, BorderLayout.CENTER);
    731           set_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
    732           set_pane.setLayout(new BorderLayout());
    733           set_pane.add(set_label, BorderLayout.WEST);
    734           set_pane.add(set, BorderLayout.CENTER);
    735           element_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
    736           element_pane.setLayout(new BorderLayout());
    737           element_pane.add(element_label, BorderLayout.WEST);
    738           element_pane.add(element, BorderLayout.CENTER);
    739           control_pane.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
    740           control_pane.setLayout(new GridLayout(3,1));
    741           control_pane.add(original_pane);
    742           control_pane.add(set_pane);
    743           control_pane.add(element_pane);
    744           button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
    745           button_pane.setLayout(new GridLayout(1,4));
    746           button_pane.add(add_button);
    747           button_pane.add(merge_button);
    748           button_pane.add(ignore_button);
    749           button_pane.add(cancel_button);
    750           content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    751           content_pane.setLayout(new BorderLayout());
    752           content_pane.add(new JScrollPane(instructions), BorderLayout.NORTH);
    753           content_pane.add(control_pane, BorderLayout.CENTER);
    754           content_pane.add(button_pane, BorderLayout.SOUTH);
    755           // Display
    756           dialog.setLocation((screen_size.width - SELECT_ELEMENT_SIZE.width) / 2, (screen_size.height - SELECT_ELEMENT_SIZE.height) / 2);
    757           dialog.show();
    758           // Deallocate everything because JDK1.4 won't.
    759           // Why, oh why did I do this?
    760           add_button.removeActionListener(add_listener);
    761           merge_button.removeActionListener(merge_listener);
    762           ignore_button.removeActionListener(ignore_listener);
    763           add_button = null;
    764           merge_button = null;
    765           ignore_button = null;
    766           add_listener = null;
    767           merge_listener = null;
    768           ignore_listener = null;
    769           // References
    770           args = null;
    771           content_pane = null;
    772           control_pane = null;
    773           instructions = null;
    774           original_pane = null;
    775           original_pane = null;
    776           original = null;
    777           element_pane = null;
    778           element_label = null;
    779           element = null;
    780           set_pane = null;
    781           set_label = null;
    782           set.removeActionListener(set_listener);
    783           set_listener = null;
    784           set = null;
    785           button_pane = null;
    786           dialog.dispose();
    787           dialog.destroy();
    788           dialog = null;
    789           // Dondage.
    790           return (ElementWrapper)result;
    791     }
    792 
    793     public boolean wasDialogCancelled() {
    794           return dialog_cancelled;
    795     }
    796 
    797     private String get(String key) {
    798           return get(key, null);
    799     }
    800 
    801     private String get(String key, String args[]) {
    802           if(key.indexOf(".") == -1) {
    803                 key = "MSMPrompt." + key;
    804           }
    805           return Gatherer.dictionary.get(key, args);
    806     }
     697        MetadataSet closest_set = null;
     698        ElementWrapper closest_element = null;
     699        int med = -1;
     700        for(int i = 0; i < set.getItemCount(); i++) {
     701        MetadataSet mds = (MetadataSet) set.getItemAt(i);
     702        for(int j = 0; j < mds.size(); j++) {
     703            ElementWrapper mde = new ElementWrapper(mds.getElement(j));
     704            int new_med = MED.LD(name, mde.toString());
     705            if(med == -1 || new_med < med) {
     706            med = new_med;
     707            closest_element = mde;
     708            closest_set = mds;
     709            }
     710            mde = null;
     711        }
     712        mds = null;
     713        }
     714        if(closest_set != null && closest_element != null) {
     715        set.setSelectedItem(closest_set);
     716        element.setSelectedItem(closest_element);
     717        set_listener.actionPerformed();
     718        }
     719        else {
     720        set_listener.actionPerformed();
     721        }
     722        closest_set = null;
     723        closest_element = null;
     724    }
     725    merge_button.setEnabled(element.getItemCount() > 0);
     726    // Layout
     727    original_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
     728    original_pane.setLayout(new BorderLayout());
     729    original_pane.add(original_label, BorderLayout.WEST);
     730    original_pane.add(original, BorderLayout.CENTER);
     731    set_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
     732    set_pane.setLayout(new BorderLayout());
     733    set_pane.add(set_label, BorderLayout.WEST);
     734    set_pane.add(set, BorderLayout.CENTER);
     735    element_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
     736    element_pane.setLayout(new BorderLayout());
     737    element_pane.add(element_label, BorderLayout.WEST);
     738    element_pane.add(element, BorderLayout.CENTER);
     739    control_pane.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
     740    control_pane.setLayout(new GridLayout(3,1));
     741    control_pane.add(original_pane);
     742    control_pane.add(set_pane);
     743    control_pane.add(element_pane);
     744    button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
     745    button_pane.setLayout(new GridLayout(1,4));
     746    button_pane.add(add_button);
     747    button_pane.add(merge_button);
     748    button_pane.add(ignore_button);
     749    button_pane.add(cancel_button);
     750    content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     751    content_pane.setLayout(new BorderLayout());
     752    content_pane.add(new JScrollPane(instructions), BorderLayout.NORTH);
     753    content_pane.add(control_pane, BorderLayout.CENTER);
     754    content_pane.add(button_pane, BorderLayout.SOUTH);
     755    // Display
     756    dialog.setLocation((screen_size.width - SELECT_ELEMENT_SIZE.width) / 2, (screen_size.height - SELECT_ELEMENT_SIZE.height) / 2);
     757    dialog.show();
     758    // Deallocate everything because JDK1.4 won't.
     759    // Why, oh why did I do this?
     760    add_button.removeActionListener(add_listener);
     761    merge_button.removeActionListener(merge_listener);
     762    ignore_button.removeActionListener(ignore_listener);
     763    add_button = null;
     764    merge_button = null;
     765    ignore_button = null;
     766    add_listener = null;
     767    merge_listener = null;
     768    ignore_listener = null;
     769    // References
     770    args = null;
     771    content_pane = null;
     772    control_pane = null;
     773    instructions = null;
     774    original_pane = null;
     775    original_pane = null;
     776    original = null;
     777    element_pane = null;
     778    element_label = null;
     779    element = null;
     780    set_pane = null;
     781    set_label = null;
     782    set.removeActionListener(set_listener);
     783    set_listener = null;
     784    set = null;
     785    button_pane = null;
     786    dialog.dispose();
     787    dialog.destroy();
     788    dialog = null;
     789    // Dondage.
     790    return (ElementWrapper)result;
     791    }
     792
     793    public boolean wasDialogCancelled() {
     794    return dialog_cancelled;
     795    }
     796
     797    private String get(String key) {
     798    return get(key, null);
     799    }
     800
     801    private String get(String key, String args[]) {
     802    if(key.indexOf(".") == -1) {
     803        key = "MSMPrompt." + key;
     804    }
     805    return Gatherer.dictionary.get(key, args);
     806    }
    807807         
    808     /** Prompts the user to select a metadata set from the given list. Uses the name parameter to attempt to automatically select the correct collection (ie the only collection that has an element with the same name).
    809       * @param name The name of the metadata element whose set name is unknown.
    810       * @return The metadata set the user has selected or null if no set selected.
    811       */
    812     final public MetadataSet selectSet(String name) {
    813           String args[] = new String[1];
    814           args[0] = name;
    815           // Creation
    816           MSMDialog dialog = new MSMDialog();
    817           dialog.setModal(true);
    818           dialog.setSize(SELECT_SET_SIZE);
    819           dialog.setTitle(get("Select_Set_Title"));
    820           JPanel content_pane = (JPanel) dialog.getContentPane();
    821           JPanel control_pane = new JPanel();
    822           JTextArea instructions = new JTextArea(get("Select_Set_Instructions", args));
    823           instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
    824           instructions.setEditable(false);
    825           instructions.setLineWrap(true);
    826           instructions.setRows(SELECT_LINE_COUNT);
    827           instructions.setWrapStyleWord(true);
    828           JComboBox set = new JComboBox();
    829           set.setBackground(Color.white);
    830           set.addItem(get("Select_Set_None"));
    831           Vector sets = manager.getSets();
    832           for(int i = sets.size() - 1; i >= 0; i--) {
    833                 set.addItem(sets.get(i));
    834           }
    835           JButton ok = new JButton(get("General.OK"));
    836           ActionListener ok_listener = new IgnoreListener(dialog);
    837           ok.addActionListener(ok_listener); // Doesn't really ignore. Just disposes()
    838           // Select most likely set.
    839           if(name.indexOf(".") != -1) {
    840                 String set_name = name.substring(0, name.indexOf("."));
    841                 MetadataSet metadata_set = manager.getSet(set_name);
    842                 if(metadata_set != null) {
    843                      set.setSelectedItem(metadata_set);
    844                 }
    845                 metadata_set = null;
    846                 set_name = null;
    847           }
    848           else {
    849                 Vector matches = manager.setsThatContain(name);
    850                 if(matches.size() == 1) {
    851                      set.setSelectedItem(matches.get(0));
    852                 }
    853                 matches = null;
    854           }
    855           // Layout
    856           control_pane.setLayout(new GridLayout(2,1));
    857           control_pane.add(set);
    858           control_pane.add(ok);
    859           content_pane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
    860           content_pane.setLayout(new BorderLayout());
    861           content_pane.add(new JScrollPane(instructions), BorderLayout.CENTER);
    862           content_pane.add(control_pane, BorderLayout.SOUTH);
    863           // Display
    864           dialog.setLocation((screen_size.width - SELECT_SET_SIZE.width) / 2, (screen_size.height - SELECT_SET_SIZE.height) / 2);
    865           dialog.show();
    866           Object value = set.getSelectedItem();
    867           if(value instanceof MetadataSet) {
    868                 return (MetadataSet) value;
    869           }
    870           value = null;
    871           ok.removeActionListener(ok_listener);
    872           ok_listener = null;
    873           ok = null;
    874           content_pane = null;
    875           control_pane = null;
    876           instructions = null;
    877           set = null;
    878           dialog.destroy();
    879           dialog = null;
    880           return null;
    881     }
    882 
    883     private class AddListener
    884           implements ActionListener {
    885           private JComboBox set = null;
    886           private JDialog dialog = null;
    887           private String name = null;
    888           public AddListener(JDialog dialog, String name, JComboBox set) {
    889                 this.dialog = dialog;
    890                 this.name = name;
    891                 this.set = set;
    892           }
    893           public void actionPerformed(ActionEvent event) {
     808    /** Prompts the user to select a metadata set from the given list. Uses the name parameter to attempt to automatically select the correct collection (ie the only collection that has an element with the same name).
     809     * @param name The name of the metadata element whose set name is unknown.
     810     * @return The metadata set the user has selected or null if no set selected.
     811     */
     812    final public MetadataSet selectSet(String name) {
     813    String args[] = new String[1];
     814    args[0] = name;
     815    // Creation
     816    MSMDialog dialog = new MSMDialog();
     817    dialog.setModal(true);
     818    dialog.setSize(SELECT_SET_SIZE);
     819    dialog.setTitle(get("Select_Set_Title"));
     820    JPanel content_pane = (JPanel) dialog.getContentPane();
     821    JPanel control_pane = new JPanel();
     822    JTextArea instructions = new JTextArea(get("Select_Set_Instructions", args));
     823    instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
     824    instructions.setEditable(false);
     825    instructions.setLineWrap(true);
     826    instructions.setRows(SELECT_LINE_COUNT);
     827    instructions.setWrapStyleWord(true);
     828    JComboBox set = new JComboBox();
     829    set.setBackground(Color.white);
     830    set.addItem(get("Select_Set_None"));
     831    Vector sets = manager.getSets();
     832    for(int i = sets.size() - 1; i >= 0; i--) {
     833        set.addItem(sets.get(i));
     834    }
     835    JButton ok = new JButton(get("General.OK"));
     836    ActionListener ok_listener = new IgnoreListener(dialog);
     837    ok.addActionListener(ok_listener); // Doesn't really ignore. Just disposes()
     838    // Select most likely set.
     839    if(name.indexOf(".") != -1) {
     840        String set_name = name.substring(0, name.indexOf("."));
     841        MetadataSet metadata_set = manager.getSet(set_name);
     842        if(metadata_set != null) {
     843        set.setSelectedItem(metadata_set);
     844        }
     845        metadata_set = null;
     846        set_name = null;
     847    }
     848    else {
     849        Vector matches = manager.setsThatContain(name);
     850        if(matches.size() == 1) {
     851        set.setSelectedItem(matches.get(0));
     852        }
     853        matches = null;
     854    }
     855    // Layout
     856    control_pane.setLayout(new GridLayout(2,1));
     857    control_pane.add(set);
     858    control_pane.add(ok);
     859    content_pane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
     860    content_pane.setLayout(new BorderLayout());
     861    content_pane.add(new JScrollPane(instructions), BorderLayout.CENTER);
     862    content_pane.add(control_pane, BorderLayout.SOUTH);
     863    // Display
     864    dialog.setLocation((screen_size.width - SELECT_SET_SIZE.width) / 2, (screen_size.height - SELECT_SET_SIZE.height) / 2);
     865    dialog.show();
     866    Object value = set.getSelectedItem();
     867    if(value instanceof MetadataSet) {
     868        return (MetadataSet) value;
     869    }
     870    value = null;
     871    ok.removeActionListener(ok_listener);
     872    ok_listener = null;
     873    ok = null;
     874    content_pane = null;
     875    control_pane = null;
     876    instructions = null;
     877    set = null;
     878    dialog.destroy();
     879    dialog = null;
     880    return null;
     881    }
     882
     883    private class AddListener
     884    implements ActionListener {
     885    private JComboBox set = null;
     886    private JDialog dialog = null;
     887    private String name = null;
     888    public AddListener(JDialog dialog, String name, JComboBox set) {
     889        this.dialog = dialog;
     890        this.name = name;
     891        this.set = set;
     892    }
     893    public void actionPerformed(ActionEvent event) {
    894894                // Get the currently selected metadata set.
    895                 MetadataSet mds = (MetadataSet) set.getSelectedItem();
    896                 String n = name;
     895        MetadataSet mds = (MetadataSet) set.getSelectedItem();
     896        String n = name;
    897897                // If we've been forced to go this far then any given namespace is complete bollocks.
    898                 while(n.indexOf(".") != -1 && !n.equals(".")) {
    899                      n = n.substring(n.indexOf(".") + 1);
    900                 }
     898        while(n.indexOf(".") != -1 && !n.equals(".")) {
     899        n = n.substring(n.indexOf(".") + 1);
     900        }
    901901                // However, before we attempt to add a new element to the set, we should check that none already exists.
    902                 Element element = mds.getElement(name);
    903                 if(element == null) {
    904                      result = mds.addElement(n);
    905                 }
    906                 else {
    907                      result = (ElementWrapper) element;
    908                 }
    909                 mds = null;
    910                 n = null;
    911                 element = null;
    912                 dialog.dispose();
    913           }
    914     }
    915     /** This listener listens for selections within the select element to merge prompt, and once one has been made enables the ok button.*/
    916     private class ElementListListener
    917           implements ListSelectionListener {
    918           /** The button we either enable or disable depending on user selection. */
    919           private JButton ok = null;
    920           /** The list from whom we are listening for selection events. */
    921           private JList list = null;
    922           /** Constructor.
    923             * @param list The <strong>JList</Strong> from whom we are listening for selection events.
    924             * @param ok The <Strong>JButton</strong> we either enable or disable depending on user selection.
    925             */
    926           public ElementListListener(JList list, JButton ok) {
    927                 this.list = list;
    928                 this.ok = ok;
    929           }
    930           /** Any implementation of <i>ListSelectionListener</i> must include this method so we can be informed when a list selection event has occured.
    931             * @param event A <strong>ListSelectionEvent</strong> generated by the event.
    932             */
    933           public void valueChanged(ListSelectionEvent event) {
    934                 if(list.getSelectedValue() != null) {
    935                      ok.setEnabled(true);
    936                 }
    937                 else {
    938                      ok.setEnabled(false);
    939                 }
    940           }
    941     }
    942 
    943     private class CancelListener
    944           implements ActionListener {
    945           private JDialog dialog = null;
    946           public CancelListener(JDialog dialog) {
    947                 this.dialog = dialog;
    948           }
    949           public void actionPerformed(ActionEvent event) {
    950                 dialog_cancelled = true;
    951                 dialog.dispose();
    952           }       
    953     }
    954 
    955     private class IgnoreListener
    956           implements ActionListener {
    957           private JDialog dialog = null;
    958           public IgnoreListener(JDialog dialog) {
    959                 this.dialog = dialog;
    960           }
    961           public void actionPerformed(ActionEvent event) {
    962                 dialog.dispose();
    963           }
    964     }
    965 
    966     private class MergeListener
    967           implements ActionListener {
    968           private JComboBox element = null;
    969           private JDialog dialog = null;
    970           public MergeListener(JDialog dialog, JComboBox element) {
    971                 this.dialog = dialog;
    972                 this.element = element;
    973           }
    974           public void actionPerformed(ActionEvent event) {
     902        Element element = mds.getElement(name);
     903        if(element == null) {
     904        result = mds.addElement(n);
     905        }
     906        else {
     907        result = (ElementWrapper) element;
     908        }
     909        mds = null;
     910        n = null;
     911        element = null;
     912        dialog.dispose();
     913    }
     914    }
     915    /** This listener listens for selections within the select element to merge prompt, and once one has been made enables the ok button.*/
     916    private class ElementListListener
     917    implements ListSelectionListener {
     918    /** The button we either enable or disable depending on user selection. */
     919    private JButton ok = null;
     920    /** The list from whom we are listening for selection events. */
     921    private JList list = null;
     922    /** Constructor.
     923     * @param list The <strong>JList</Strong> from whom we are listening for selection events.
     924     * @param ok The <Strong>JButton</strong> we either enable or disable depending on user selection.
     925     */
     926    public ElementListListener(JList list, JButton ok) {
     927        this.list = list;
     928        this.ok = ok;
     929    }
     930    /** Any implementation of <i>ListSelectionListener</i> must include this method so we can be informed when a list selection event has occured.
     931     * @param event A <strong>ListSelectionEvent</strong> generated by the event.
     932     */
     933    public void valueChanged(ListSelectionEvent event) {
     934        if(list.getSelectedValue() != null) {
     935        ok.setEnabled(true);
     936        }
     937        else {
     938        ok.setEnabled(false);
     939        }
     940    }
     941    }
     942
     943    private class CancelListener
     944    implements ActionListener {
     945    private JDialog dialog = null;
     946    public CancelListener(JDialog dialog) {
     947        this.dialog = dialog;
     948    }
     949    public void actionPerformed(ActionEvent event) {
     950        dialog_cancelled = true;
     951        dialog.dispose();
     952    }         
     953    }
     954
     955    private class IgnoreListener
     956    implements ActionListener {
     957    private JDialog dialog = null;
     958    public IgnoreListener(JDialog dialog) {
     959        this.dialog = dialog;
     960    }
     961    public void actionPerformed(ActionEvent event) {
     962        dialog.dispose();
     963    }
     964    }
     965
     966    private class MergeListener
     967    implements ActionListener {
     968    private JComboBox element = null;
     969    private JDialog dialog = null;
     970    public MergeListener(JDialog dialog, JComboBox element) {
     971        this.dialog = dialog;
     972        this.element = element;
     973    }
     974    public void actionPerformed(ActionEvent event) {
    975975                // Return the currently selected element
    976                 result = (ElementWrapper)element.getSelectedItem();
    977                 dialog.dispose();
    978           }
    979     }
    980 
    981     private class MSMDialog
    982           extends JDialog {
    983           public void destroy() {
    984                 rootPane = null;
    985           }
    986     }
    987 
    988     private class SetListener
    989           implements ActionListener {
    990           private JButton add_button = null;
    991           private JButton merge_button = null;
    992           private JComboBox element = null;
    993           private JComboBox set = null;
    994           private String name = null;
    995           public SetListener(JComboBox set, JComboBox element, String name, JButton add_button, JButton merge_button) {
    996                 this.add_button = add_button;
    997                 this.element = element;
    998                 this.merge_button = merge_button;
    999                 this.name = name.toLowerCase();
    1000                 this.set = set;
    1001           }
    1002           public void actionPerformed() {
    1003                 element.removeAllItems();
    1004                 MetadataSet mds = (MetadataSet) set.getSelectedItem();
    1005                 System.err.println("Set selected: " + mds);
    1006                 if(mds != null) {
    1007                      NodeList elements = mds.getElements();
    1008                      Vector temp = new Vector();
    1009                      for(int i = elements.getLength() - 1; i >= 0; i--) {
    1010                           temp.add(new ElementWrapper((Element)elements.item(i)));
    1011                      }
     976        result = (ElementWrapper)element.getSelectedItem();
     977        dialog.dispose();
     978    }
     979    }
     980
     981    private class MSMDialog
     982    extends JDialog {
     983    public void destroy() {
     984        rootPane = null;
     985    }
     986    }
     987
     988    private class SetListener
     989    implements ActionListener {
     990    private JButton add_button = null;
     991    private JButton merge_button = null;
     992    private JComboBox element = null;
     993    private JComboBox set = null;
     994    private String name = null;
     995    public SetListener(JComboBox set, JComboBox element, String name, JButton add_button, JButton merge_button) {
     996        this.add_button = add_button;
     997        this.element = element;
     998        this.merge_button = merge_button;
     999        this.name = name.toLowerCase();
     1000        this.set = set;
     1001    }
     1002    public void actionPerformed() {
     1003        element.removeAllItems();
     1004        MetadataSet mds = (MetadataSet) set.getSelectedItem();
     1005        System.err.println("Set selected: " + mds);
     1006        if(mds != null) {
     1007        NodeList elements = mds.getElements();
     1008        Vector temp = new Vector();
     1009        for(int i = elements.getLength() - 1; i >= 0; i--) {
     1010            temp.add(new ElementWrapper((Element)elements.item(i)));
     1011        }
    10121012                     
    1013                      // The add button is only enabled if an element with the same name doesn't already exist in the destination set.
    1014                      boolean add_button_enabled = true;
    1015                      ///ystem.err.println("Checking for " + name);
    1016                      for(int j = 0; j < temp.size(); j++) {
    1017                           ElementWrapper an_element = (ElementWrapper)temp.get(j);
    1018                           // Can only add if no file with the same name exists.
    1019                           String check = an_element.toString().toLowerCase();
    1020                           int position = -1;
    1021                           if((position = check.indexOf(".")) != -1) {
    1022                                 check = check.substring(position + 1);
    1023                           }
    1024                           ///ystem.err.println("does it match " + check + "? " + name.equals(check));
    1025                           add_button_enabled = add_button_enabled && !name.equals(check);
    1026                           check = null;
    1027                           element.addItem(an_element);
    1028                           an_element = null;
    1029                      }
    1030                      add_button.setEnabled(add_button_enabled);
    1031 
    1032                      // The merge button is only enabled if there is more than one element in the destination set.
    1033                      if(temp.size() > 0) {
    1034                           merge_button.setEnabled(true);
    1035                      }
    1036                      else {
    1037                           merge_button.setEnabled(false);
    1038                      }
    1039                      temp = null;
    1040                      elements = null;
    1041                 }
    1042                 else {
    1043                      add_button.setEnabled(false);
    1044                      merge_button.setEnabled(false);
    1045                 }
    1046                 mds = null;
    1047           }
    1048           public void actionPerformed(ActionEvent item) {
    1049                 actionPerformed();
    1050          
    1051     }
     1013        // The add button is only enabled if an element with the same name doesn't already exist in the destination set.
     1014        boolean add_button_enabled = true;
     1015        ///ystem.err.println("Checking for " + name);
     1016        for(int j = 0; j < temp.size(); j++) {
     1017            ElementWrapper an_element = (ElementWrapper)temp.get(j);
     1018            // Can only add if no file with the same name exists.
     1019            String check = an_element.toString().toLowerCase();
     1020            int position = -1;
     1021            if((position = check.indexOf(".")) != -1) {
     1022            check = check.substring(position + 1);
     1023            }
     1024            ///ystem.err.println("does it match " + check + "? " + name.equals(check));
     1025            add_button_enabled = add_button_enabled && !name.equals(check);
     1026            check = null;
     1027            element.addItem(an_element);
     1028            an_element = null;
     1029        }
     1030        add_button.setEnabled(add_button_enabled);
     1031
     1032        // The merge button is only enabled if there is more than one element in the destination set.
     1033        if(temp.size() > 0) {
     1034            merge_button.setEnabled(true);
     1035        }
     1036        else {
     1037            merge_button.setEnabled(false);
     1038        }
     1039        temp = null;
     1040        elements = null;
     1041        }
     1042        else {
     1043        add_button.setEnabled(false);
     1044        merge_button.setEnabled(false);
     1045        }
     1046        mds = null;
     1047    }
     1048    public void actionPerformed(ActionEvent item) {
     1049        actionPerformed();
     1050   
     1051    }
    10521052}
    1053 
  • trunk/gli/src/org/greenstone/gatherer/msm/MSMUtils.java

    r4330 r4365  
    6767 */
    6868public class MSMUtils {
    69     /** Used to order metadata according to set standard element order then alphabetically by value. */
    70     static public MetadataComparator METADATA_COMPARATOR = new MetadataComparator();
    71     /** An element of the enumeration of type filter. */
    72     static public final int NONE = 0;
    73     /** An element of the enumeration of type filter. */
    74     static public final int VALUES = 1;
    75     /** An element of the enumeration of type filter. */
    76     static public final int ALIASES = 2;
    77     /** An element of the enumeration of type filter. */
    78     static public final int BOTH = 3;
    79     /** The character used to separate name space from metadata element. */
    80     static public final char NS_SEP= '.';
    81     /** Method to add one node as a child of another, after migrating into the target document.
    82       * @param parent The <strong>Node</strong> we are inserting into.
    83       * @param child The original <strong>Node</strong> we are inserting. Must first be cloned into the parents document.
    84       */
    85     static final public void add(Node parent, Node child) {
    86           Document document = parent.getOwnerDocument();
    87           Node new_child = document.importNode(child, true);
    88           parent.appendChild(new_child);
    89     }
    90 
    91     static final public void addElementAttribute(Node node, String name, String language, String value) {
    92           Document document = node.getOwnerDocument();
    93           Element attribute_node = document.createElementNS("", "Attribute");
    94           attribute_node.setAttribute("name", name);
    95           attribute_node.setAttribute("language", language);
    96           node.appendChild(attribute_node);
    97           Node attribute_text = document.createTextNode(value);
    98           attribute_node.appendChild(attribute_text);
    99     }
    100 
    101     /** A method for comparing two AssignedValues trees. This compares not only the Subject hierarchies but also the values themselves.
    102       * @param avt A <strong>Node</strong> which is the root of an AssignedValues tree.
    103       * @param bvt The <strong>Node</strong> which is the root of the tree we wish to compare it to.
    104       * @return <i>true</i> if the two trees are equal, <i>false</i> otherwise.
    105       */
    106     static final public boolean assignedValuesEqual(Node avt, Node bvt) {
    107           if(avt == null && bvt == null) {
    108                 return true; // Both are null so both are equal.
    109           }
    110           else if(avt == null || bvt == null) {
     69    /** Used to order metadata according to set standard element order then alphabetically by value. */
     70    static public MetadataComparator METADATA_COMPARATOR = new MetadataComparator();
     71    /** An element of the enumeration of type filter. */
     72    static public final int NONE = 0;
     73    /** An element of the enumeration of type filter. */
     74    static public final int VALUES = 1;
     75    /** An element of the enumeration of type filter. */
     76    static public final int ALIASES = 2;
     77    /** An element of the enumeration of type filter. */
     78    static public final int BOTH = 3;
     79    /** The character used to separate name space from metadata element. */
     80    static public final char NS_SEP= '.';
     81    /** Method to add one node as a child of another, after migrating into the target document.
     82     * @param parent The <strong>Node</strong> we are inserting into.
     83     * @param child The original <strong>Node</strong> we are inserting. Must first be cloned into the parents document.
     84     */
     85    static final public void add(Node parent, Node child) {
     86    Document document = parent.getOwnerDocument();
     87    Node new_child = document.importNode(child, true);
     88    parent.appendChild(new_child);
     89    }
     90
     91    static final public void addElementAttribute(Node node, String name, String language, String value) {
     92    Document document = node.getOwnerDocument();
     93    Element attribute_node = document.createElementNS("", "Attribute");
     94    attribute_node.setAttribute("name", name);
     95    attribute_node.setAttribute("language", language);
     96    node.appendChild(attribute_node);
     97    Node attribute_text = document.createTextNode(value);
     98    attribute_node.appendChild(attribute_text);
     99    }
     100
     101    /** A method for comparing two AssignedValues trees. This compares not only the Subject hierarchies but also the values themselves.
     102     * @param avt A <strong>Node</strong> which is the root of an AssignedValues tree.
     103     * @param bvt The <strong>Node</strong> which is the root of the tree we wish to compare it to.
     104     * @return <i>true</i> if the two trees are equal, <i>false</i> otherwise.
     105     */
     106    static final public boolean assignedValuesEqual(Node avt, Node bvt) {
     107    if(avt == null && bvt == null) {
     108        return true; // Both are null so both are equal.
     109    }
     110    else if(avt == null || bvt == null) {
    111111                // One is null and the other isn't.
    112                 return false;
    113           }
    114           else {
    115                 Hashtable a_map = new Hashtable();
    116                 getValueMappings(avt, null, a_map);
    117                 Hashtable b_map = new Hashtable();
    118                 getValueMappings(bvt, null, b_map);
    119                 if(a_map.size() == b_map.size()) {
    120                      /** @TODO - Figure out what to do now. */
    121                      return true;
    122                 }
    123           }
    124           return false;
    125     }
    126     /** A method for comparing two attribute nodes of type Node not Attr as you might think. Attr objects are used to describe the attributes of tags themselves, while in a metadata set we intend attribute nodes to describe qualities of metadata elements. It's just confusing because the two systems (DOM model and Dublin Core) are quite similar.
    127       * @param an A <strong>Node</strong> representing some attribute of an element.
    128       * @param bn The <strong>Node</strong> we wish to compare it to.
    129       * @return <i>true</i> if and only if the attributes are equal.
    130       */
    131     static final public boolean attributesEqual(Node an, Node bn) {
    132           // Check we are comparing apples and apples...
    133           if(an.getNodeName().equals("Attribute") && bn.getNodeName().equals("Attribute")) {
    134                 Element ae = (Element) an;
    135                 Element be = (Element) bn;
     112        return false;
     113    }
     114    else {
     115        Hashtable a_map = new Hashtable();
     116        getValueMappings(avt, null, a_map);
     117        Hashtable b_map = new Hashtable();
     118        getValueMappings(bvt, null, b_map);
     119        if(a_map.size() == b_map.size()) {
     120        /** @TODO - Figure out what to do now. */
     121        return true;
     122        }
     123    }
     124    return false;
     125    }
     126    /** A method for comparing two attribute nodes of type Node not Attr as you might think. Attr objects are used to describe the attributes of tags themselves, while in a metadata set we intend attribute nodes to describe qualities of metadata elements. It's just confusing because the two systems (DOM model and Dublin Core) are quite similar.
     127     * @param an A <strong>Node</strong> representing some attribute of an element.
     128     * @param bn The <strong>Node</strong> we wish to compare it to.
     129     * @return <i>true</i> if and only if the attributes are equal.
     130     */
     131    static final public boolean attributesEqual(Node an, Node bn) {
     132    // Check we are comparing apples and apples...
     133    if(an.getNodeName().equals("Attribute") && bn.getNodeName().equals("Attribute")) {
     134        Element ae = (Element) an;
     135        Element be = (Element) bn;
    136136                // Ensure we are comparing the same type of attribute.
    137                 if(ae.getAttribute("name").equals(be.getAttribute("name"))) {
    138                      // And finally retrieve and compare the values.
    139                      if(getValue(ae).equals(getValue(be))) {
    140                           // We have a match.
    141                           return true;
    142                      }
    143                 }
    144           }
    145           // And if anything goes wrong we can't be dealing with equal attributes.
    146           return false;
    147     }
    148     /** Method to determine if a certain path of elements can be found in another tree. This method tests by comparing node names, and finally the #text node past the leaf end of the path.
    149       * @param tree The root <strong>Node</strong> of the tree to be searched.
    150       * @param path The <strong>Node[]</strong> path to be found.
    151       * @return <i>true</i> if the path was found (including matching #text elements at the leaf), or <i>false</i> otherwise.
    152       */
    153     static final public boolean containsPath(Node tree, Node path[]) {
    154           // If there is no tree then there are no values.
    155           if(tree == null) {
    156                 return false;
    157           }
    158           // Ensure we are comparing equivent things.
    159           if(tree.getNodeName().equals("AssignedValues") && path[0].getNodeName().equals("AssignedValues")) {
    160                 int index = 1;
    161                 while(index < path.length && tree != null) {
    162                      Node next = null;
    163                      for(Node n = tree.getFirstChild(); n != null && next == null; n = n.getNextSibling()) {
    164                           if(n.getNodeName().equals(path[index].getNodeName())) {
    165                                 next = n;
    166                                 index++;
    167                           }
    168                           tree = next;
    169                      }
    170                 }
     137        if(ae.getAttribute("name").equals(be.getAttribute("name"))) {
     138        // And finally retrieve and compare the values.
     139        if(getValue(ae).equals(getValue(be))) {
     140            // We have a match.
     141            return true;
     142        }
     143        }
     144    }
     145    // And if anything goes wrong we can't be dealing with equal attributes.
     146    return false;
     147    }
     148    /** Method to determine if a certain path of elements can be found in another tree. This method tests by comparing node names, and finally the #text node past the leaf end of the path.
     149     * @param tree The root <strong>Node</strong> of the tree to be searched.
     150     * @param path The <strong>Node[]</strong> path to be found.
     151     * @return <i>true</i> if the path was found (including matching #text elements at the leaf), or <i>false</i> otherwise.
     152     */
     153    static final public boolean containsPath(Node tree, Node path[]) {
     154    // If there is no tree then there are no values.
     155    if(tree == null) {
     156        return false;
     157    }
     158    // Ensure we are comparing equivent things.
     159    if(tree.getNodeName().equals("AssignedValues") && path[0].getNodeName().equals("AssignedValues")) {
     160        int index = 1;
     161        while(index < path.length && tree != null) {
     162        Node next = null;
     163        for(Node n = tree.getFirstChild(); n != null && next == null; n = n.getNextSibling()) {
     164            if(n.getNodeName().equals(path[index].getNodeName())) {
     165            next = n;
     166            index++;
     167            }
     168            tree = next;
     169        }
     170        }
    171171                // Tree may now be pointing at the same node as
    172172                // path[path.length - 1] so we should test their child text nodes.
    173                 if(tree != null) {
    174                      Node tree_text = tree.getFirstChild();
    175                      Node path_text = path[path.length - 1].getFirstChild();
    176                      if(tree_text.getNodeValue().equals(path_text.getNodeValue())) {
    177                           // Path found!
    178                           return true;
    179                      }
    180                 }
    181           }
    182           return false;     
    183     }
    184     /** Method to compare two metadata elements (of type Element, which is bound to get more than a bit confusing) for equality. This test may only check the structural (ie pretty much unchanging) consistancy, or may include the AssignedValue tree as well (which will be different for each collection I'd imagine).
    185       * @param a_set The <strong>MetadataSet</strong> a comes from.
    186       * @param a An <strong>Element</strong>.
    187       * @param b_set The <strong>MetadataSet</strong> b comes from.
    188       * @param b The <strong>Element</strong> to compare it to.
    189       * @param values <i>true</i> if the AssignedValues tree should also be compared, <i>false</i> otherwise.
    190       * @return <i>true</i> if the elements are equal, <i>false</i> otherwise.
    191       */
    192     static final public boolean elementsEqual(MetadataSet a_set, Element ae, MetadataSet b_set, Element be, boolean values) {
    193           // Compare Element Attr(ibutes) within the DOM, not to be confused with comparing element attributes in a Dublin Core sense...
    194           NamedNodeMap aas = ae.getAttributes();
    195           NamedNodeMap bas = be.getAttributes();
    196           // For each attribute in a...
    197           for(int i = 0; i < aas.getLength(); i++) {
    198                 Attr aa = (Attr)aas.item(i);
     173        if(tree != null) {
     174        Node tree_text = tree.getFirstChild();
     175        Node path_text = path[path.length - 1].getFirstChild();
     176        if(tree_text.getNodeValue().equals(path_text.getNodeValue())) {
     177            // Path found!
     178            return true;
     179        }
     180        }
     181    }
     182    return false;       
     183    }
     184    /** Method to compare two metadata elements (of type Element, which is bound to get more than a bit confusing) for equality. This test may only check the structural (ie pretty much unchanging) consistancy, or may include the AssignedValue tree as well (which will be different for each collection I'd imagine).
     185     * @param a_set The <strong>MetadataSet</strong> a comes from.
     186     * @param a An <strong>Element</strong>.
     187     * @param b_set The <strong>MetadataSet</strong> b comes from.
     188     * @param b The <strong>Element</strong> to compare it to.
     189     * @param values <i>true</i> if the AssignedValues tree should also be compared, <i>false</i> otherwise.
     190     * @return <i>true</i> if the elements are equal, <i>false</i> otherwise.
     191     */
     192    static final public boolean elementsEqual(MetadataSet a_set, Element ae, MetadataSet b_set, Element be, boolean values) {
     193    // Compare Element Attr(ibutes) within the DOM, not to be confused with comparing element attributes in a Dublin Core sense...
     194    NamedNodeMap aas = ae.getAttributes();
     195    NamedNodeMap bas = be.getAttributes();
     196    // For each attribute in a...
     197    for(int i = 0; i < aas.getLength(); i++) {
     198        Attr aa = (Attr)aas.item(i);
    199199                // Try to retrieve an attribute of the same name from b.
    200                 Attr ba = (Attr)bas.getNamedItem(aa.getNodeName());
     200        Attr ba = (Attr)bas.getNamedItem(aa.getNodeName());
    201201                // Now if there was no such attribute, or if the values for the
    202202                // two attributes are different the structures different.
    203                 if(ba == null || (!aa.getValue().equals(ba.getValue()))) {
    204                      //ystem.err.println("Attributes are not equal");
    205                      return false;
    206                 }
    207           }
    208           // Quickest test of children is to see we have the same number in
    209           // each. Remember to modify for missing AssignedValues which have
    210           // nothing to do with structure.
    211           int anc = getAttributeCount(ae);
    212           int bnc = getAttributeCount(be);
    213           if(anc != bnc) {
    214                 return false;
    215           }
    216           // Now we compare the child nodes of the two Elements taking into
    217           // account three special cases...
    218           // 1. We don't test the AssignedValues element here.
    219           // 2. Remember OptionList node.
    220           // 3. The Attributes of each metadata element.
    221           // For each child node of a.
    222           for(Node an = ae.getFirstChild(); an !=null; an =an.getNextSibling()) {
    223                 if(an.getNodeName().equals("OptionList")) {
    224                      //ystem.err.println("Matching OptionLists.");
    225                      Node bn = getNodeFromNamed(be, "OptionList");
    226                      if(bn == null || !optionListsEqual(an, bn)) {
    227                           //ystem.err.println("OptionLists are not equal");
    228                           return false;
    229                      }
    230                 }
     203        if(ba == null || (!aa.getValue().equals(ba.getValue()))) {
     204        //ystem.err.println("Attributes are not equal");
     205        return false;
     206        }
     207    }
     208    // Quickest test of children is to see we have the same number in
     209    // each. Remember to modify for missing AssignedValues which have
     210    // nothing to do with structure.
     211    int anc = getAttributeCount(ae);
     212    int bnc = getAttributeCount(be);
     213    if(anc != bnc) {
     214        return false;
     215    }
     216    // Now we compare the child nodes of the two Elements taking into
     217    // account three special cases...
     218    // 1. We don't test the AssignedValues element here.
     219    // 2. Remember OptionList node.
     220    // 3. The Attributes of each metadata element.
     221    // For each child node of a.
     222    for(Node an = ae.getFirstChild(); an !=null; an =an.getNextSibling()) {
     223        if(an.getNodeName().equals("OptionList")) {
     224        //ystem.err.println("Matching OptionLists.");
     225        Node bn = getNodeFromNamed(be, "OptionList");
     226        if(bn == null || !optionListsEqual(an, bn)) {
     227            //ystem.err.println("OptionLists are not equal");
     228            return false;
     229        }
     230        }
    231231                // Matching attributes.
    232                 else if(an.getNodeName().equals("Attribute")) {
    233                      //ystem.err.println("Matching Attributes.");
    234                      boolean matched = false;
    235                      for(Node bn = be.getFirstChild(); bn != null && !matched;
    236                           bn = bn.getNextSibling()) {
    237                           if(bn.getNodeName().equals("Attribute")) {
    238                                 matched = attributesEqual(an, bn);
    239                           }
    240                      }
    241                      if(!matched) {
    242                           //ystem.err.println("Cannot match attribute.");
    243                           return false;
    244                      }
    245                 }
    246           }
    247           // Finally, if we've been asked to compares value trees (for some unknown reason) go ahead and compare them too.
    248           if(values) {
    249                 GValueModel avt = a_set.getValueTree(new ElementWrapper(ae));
    250                 GValueModel bvt = b_set.getValueTree(new ElementWrapper(be));
    251                 return assignedValuesEqual(avt.getDocument().getDocumentElement(), bvt.getDocument().getDocumentElement());
    252           }
    253           // If we've got this far the elements match!
    254           return true;
    255    
    256     /** This method extracts the assigned value trees details, if any, from a certain element and stores them in an array ready to be passed as arguments to the Dictionary.
    257       * @param element The <strong>Element</strong> whose values we wish to view.
    258       * @return A <strong>String[]</strong> containing the details of the assigned values tree.
    259       * @see org.greenstone.gatherer.Dictionary
    260       */
    261     static final public String[] getAssignedValuesDetails(MetadataSet mds, Element element) {
    262           String details[] = null;
    263           //Node avt = getNodeFromNamed(element, "AssignedValues");
    264           GValueModel avt = mds.getValueTree(new ElementWrapper(element));
    265           if(avt != null) {
    266                 Hashtable mapping = new Hashtable();
    267                 getValueMappings(avt.getDocument().getDocumentElement(), null, mapping);
    268                 ArrayList values = new ArrayList(mapping.keySet());
    269                 Collections.sort(values);
    270                 details = new String[1];
    271                 for(int i = 0; i < values.size(); i++) {
    272                      if(details[0] == null) {
    273                           details[0] = "   " + values.get(i);
    274                      }
    275                      else {
    276                           details[0] = details[0] + "\n   " + values.get(i);
    277                      }
    278                 }
    279                 mapping = null;
    280                 values = null;
    281           }
    282           avt = null;
    283           return details;
    284     }
    285 
    286     static final public TreeSet getAttributes(Element element) {
    287           TreeSet attributes = new TreeSet();
    288           for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) {
    289                 if(n.getNodeName().equals("Attribute")) {
    290                      Element e = (Element)n;
    291                      attributes.add(new Attribute(e.getAttribute("name"), e.getAttribute("language"), getValue(e)));
    292                 }
    293           }
    294           return attributes;
    295     }
    296 
    297     /** Method to count the number of Attribute nodes under a certain Element. This ignores other nodes such as #text, OptionList and AssignedValues nodes.
    298       * @param element The <strong>Element</strong> whose attributes you want to count.
    299       * @return An <i>int</i> which is the number of attribute nodes.
    300       */
    301     static final public int getAttributeCount(Node element) {
    302           int count = 0;
    303           for(Node n = element.getFirstChild(); n != null;
    304                 n = n.getNextSibling()) {
    305                 if(n.getNodeName().equals("Attribute")) {
    306                      count++;
    307                 }
    308           }
    309           return count;
    310     }
    311 
    312     /*************************************************************************/
     232        else if(an.getNodeName().equals("Attribute")) {
     233        //ystem.err.println("Matching Attributes.");
     234        boolean matched = false;
     235        for(Node bn = be.getFirstChild(); bn != null && !matched;
     236            bn = bn.getNextSibling()) {
     237            if(bn.getNodeName().equals("Attribute")) {
     238            matched = attributesEqual(an, bn);
     239            }
     240        }
     241        if(!matched) {
     242            //ystem.err.println("Cannot match attribute.");
     243            return false;
     244        }
     245        }
     246    }
     247    // Finally, if we've been asked to compares value trees (for some unknown reason) go ahead and compare them too.
     248    if(values) {
     249        GValueModel avt = a_set.getValueTree(new ElementWrapper(ae));
     250        GValueModel bvt = b_set.getValueTree(new ElementWrapper(be));
     251        return assignedValuesEqual(avt.getDocument().getDocumentElement(), bvt.getDocument().getDocumentElement());
     252    }
     253    // If we've got this far the elements match!
     254    return true;
     255    }   
     256    /** This method extracts the assigned value trees details, if any, from a certain element and stores them in an array ready to be passed as arguments to the Dictionary.
     257     * @param element The <strong>Element</strong> whose values we wish to view.
     258     * @return A <strong>String[]</strong> containing the details of the assigned values tree.
     259     * @see org.greenstone.gatherer.Dictionary
     260     */
     261    static final public String[] getAssignedValuesDetails(MetadataSet mds, Element element) {
     262    String details[] = null;
     263    //Node avt = getNodeFromNamed(element, "AssignedValues");
     264    GValueModel avt = mds.getValueTree(new ElementWrapper(element));
     265    if(avt != null) {
     266        Hashtable mapping = new Hashtable();
     267        getValueMappings(avt.getDocument().getDocumentElement(), null, mapping);
     268        ArrayList values = new ArrayList(mapping.keySet());
     269        Collections.sort(values);
     270        details = new String[1];
     271        for(int i = 0; i < values.size(); i++) {
     272        if(details[0] == null) {
     273            details[0] = "   " + values.get(i);
     274        }
     275        else {
     276            details[0] = details[0] + "\n   " + values.get(i);
     277        }
     278        }
     279        mapping = null;
     280        values = null;
     281    }
     282    avt = null;
     283    return details;
     284    }
     285
     286    static final public TreeSet getAttributes(Element element) {
     287    TreeSet attributes = new TreeSet();
     288    for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) {
     289        if(n.getNodeName().equals("Attribute")) {
     290        Element e = (Element)n;
     291        attributes.add(new Attribute(e.getAttribute("name"), e.getAttribute("language"), getValue(e)));
     292        }
     293    }
     294    return attributes;
     295    }
     296
     297    /** Method to count the number of Attribute nodes under a certain Element. This ignores other nodes such as #text, OptionList and AssignedValues nodes.
     298     * @param element The <strong>Element</strong> whose attributes you want to count.
     299     * @return An <i>int</i> which is the number of attribute nodes.
     300     */
     301    static final public int getAttributeCount(Node element) {
     302    int count = 0;
     303    for(Node n = element.getFirstChild(); n != null;
     304        n = n.getNextSibling()) {
     305        if(n.getNodeName().equals("Attribute")) {
     306        count++;
     307        }
     308    }
     309    return count;
     310    }
     311
     312    /*************************************************************************/
    313313     
    314     /** This method is a slight variation on getNodeNamed in that it is especially written to retrieve the attribute Nodes of a certain name present under the given element. Note that this method is language specific.
    315       * @param element The target element <strong>Node</strong>.
    316       * @param name The name of the attribute you wish to return.
    317       * @return An <strong>Element[]</strong> containing the attributes you requested, or <i>null</i> if no such attributes exists.
    318       */
    319     static final public Element getAttributeNodeNamed(Node element, String name) {
    320           Element attribute = null;
    321           String language_code = Locale.getDefault().getLanguage();
    322           for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) {
    323                 if(n.getNodeName().equals("Attribute")) {
    324                      Element e = (Element)n;
    325                      if(e.getAttribute("name").equals(name)) {
    326                           if(e.getAttribute("language").equalsIgnoreCase(language_code)) {
    327                                 return e;
    328                           }
    329                           else if(e.getAttribute("language").equalsIgnoreCase("en")) {
    330                                 attribute = e;
    331                           }
    332                           else if(attribute == null) {
    333                                 attribute = e;
    334                           }
    335                      }
    336                      e = null;
    337                 }
    338           }
    339           return attribute;
    340     }
    341 
    342     /** This method is a slight variation on getNodeNamed in that it is especially written to retrieve the attribute Nodes of a certain name present under the given element.
    343       * @param element The target element <strong>Node</strong>.
    344       * @param name The name of the attribute you wish to return.
    345       * @return An <strong>Element[]</strong> containing the attributes you requested, or <i>null</i> if no such attributes exists.
    346       */
    347     static final public Element[] getAttributeNodesNamed(Node element, String name) {
    348           Element attributes[] = null;
    349           for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) {
    350                 if(n.getNodeName().equals("Attribute")) {
    351                      Element e = (Element)n;
    352                      if(e.getAttribute("name").equals(name)) {
    353                           if(attributes == null) {
    354                                 attributes = new Element[1];
    355                                 attributes[0] = e;
    356                           }
    357                           else {
    358                                 Element temp[] = attributes;
    359                                 attributes = new Element[temp.length + 1];
    360                                 System.arraycopy(temp, 0, attributes, 0, temp.length);
    361                                 attributes[temp.length] = e;
    362                                 temp = null;
    363                           }
    364                      }
    365                      e = null;
    366                 }
    367           }
    368           return attributes;
    369     }
    370 
    371     /** Method to construct an elements description by retrieving the correct attribute.
    372       * @param element The <strong>Element</strong> whose name we wish to retrieve.
    373       * @return A <strong>String</strong> which is the elements description, or an empty string if no description exists.
    374       */
    375     static final public String getDescription(Node element) {
    376           String definition = "";
    377           Element definition_node = getAttributeNodeNamed(element, "definition");
    378           if(definition_node != null) {
    379                 definition = getValue(definition_node);
    380           }
    381           String comment = "";
    382           Element comment_node = getAttributeNodeNamed(element, "comment");
    383           if(comment_node != null) {
    384                 comment = getValue(comment_node);
    385           }
    386           String description = definition + comment;
    387           return Utility.stripNL(description.trim());
    388     }
    389 
    390     /** Extracts the file name pattern from within a fileset of a Greenstone Directory Metadata model.
    391       * @param fileset The fileset Node in question.
    392       * @return The pattern as a String.
    393       */
    394     static final public String getFileNamePattern(Node fileset) {
    395           // Locate the child node called filename
    396           for(Node child = fileset.getFirstChild(); child != null; child = child.getNextSibling()) {
    397                 if(child.getNodeName().equalsIgnoreCase("FileName")) {
    398                      // Find the file string.
    399                      return MSMUtils.getValue(child);
    400                 }
    401           }       
    402           return null;
    403     }
    404 
    405     /*************************************************************************/
    406     /** Method to create the fully namespace quantified identifier for this element.
    407       * @param element The <strong>Node</strong> in question.
    408       * @return A fully qualified identifier as a <strong>String</strong>
    409       */
    410     static final public String getFullIdentifier(Node element) {
    411           if(element == null) {
    412                 return "Error";
    413           }
    414           // Get namespace for given element.
    415           Element root = (Element)element.getParentNode();
    416           if(root != null) {
    417                 String identifier = root.getAttribute("namespace");
     314    /** This method is a slight variation on getNodeNamed in that it is especially written to retrieve the attribute Nodes of a certain name present under the given element. Note that this method is language specific.
     315     * @param element The target element <strong>Node</strong>.
     316     * @param name The name of the attribute you wish to return.
     317     * @return An <strong>Element[]</strong> containing the attributes you requested, or <i>null</i> if no such attributes exists.
     318     */
     319    static final public Element getAttributeNodeNamed(Node element, String name) {
     320    Element attribute = null;
     321    String language_code = Locale.getDefault().getLanguage();
     322    for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) {
     323        if(n.getNodeName().equals("Attribute")) {
     324        Element e = (Element)n;
     325        if(e.getAttribute("name").equals(name)) {
     326            if(e.getAttribute("language").equalsIgnoreCase(language_code)) {
     327            return e;
     328            }
     329            else if(e.getAttribute("language").equalsIgnoreCase("en")) {
     330            attribute = e;
     331            }
     332            else if(attribute == null) {
     333            attribute = e;
     334            }
     335        }
     336        e = null;
     337        }
     338    }
     339    return attribute;
     340    }
     341
     342    /** This method is a slight variation on getNodeNamed in that it is especially written to retrieve the attribute Nodes of a certain name present under the given element.
     343     * @param element The target element <strong>Node</strong>.
     344     * @param name The name of the attribute you wish to return.
     345     * @return An <strong>Element[]</strong> containing the attributes you requested, or <i>null</i> if no such attributes exists.
     346     */
     347    static final public Element[] getAttributeNodesNamed(Node element, String name) {
     348    Element attributes[] = null;
     349    for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) {
     350        if(n.getNodeName().equals("Attribute")) {
     351        Element e = (Element)n;
     352        if(e.getAttribute("name").equals(name)) {
     353            if(attributes == null) {
     354            attributes = new Element[1];
     355            attributes[0] = e;
     356            }
     357            else {
     358            Element temp[] = attributes;
     359            attributes = new Element[temp.length + 1];
     360            System.arraycopy(temp, 0, attributes, 0, temp.length);
     361            attributes[temp.length] = e;
     362            temp = null;
     363            }
     364        }
     365        e = null;
     366        }
     367    }
     368    return attributes;
     369    }
     370
     371    /** Method to construct an elements description by retrieving the correct attribute.
     372     * @param element The <strong>Element</strong> whose name we wish to retrieve.
     373     * @return A <strong>String</strong> which is the elements description, or an empty string if no description exists.
     374     */
     375    static final public String getDescription(Node element) {
     376    String definition = "";
     377    Element definition_node = getAttributeNodeNamed(element, "definition");
     378    if(definition_node != null) {
     379        definition = getValue(definition_node);
     380    }
     381    String comment = "";
     382    Element comment_node = getAttributeNodeNamed(element, "comment");
     383    if(comment_node != null) {
     384        comment = getValue(comment_node);
     385    }
     386    String description = definition + comment;
     387    return Utility.stripNL(description.trim());
     388    }
     389
     390    /** Extracts the file name pattern from within a fileset of a Greenstone Directory Metadata model.
     391     * @param fileset The fileset Node in question.
     392     * @return The pattern as a String.
     393     */
     394    static final public String getFileNamePattern(Node fileset) {
     395    // Locate the child node called filename
     396    for(Node child = fileset.getFirstChild(); child != null; child = child.getNextSibling()) {
     397        if(child.getNodeName().equalsIgnoreCase("FileName")) {
     398        // Find the file string.
     399        return MSMUtils.getValue(child);
     400        }
     401    }         
     402    return null;
     403    }
     404
     405    /*************************************************************************/
     406    /** Method to create the fully namespace quantified identifier for this element.
     407     * @param element The <strong>Node</strong> in question.
     408     * @return A fully qualified identifier as a <strong>String</strong>
     409     */
     410    static final public String getFullIdentifier(Node element) {
     411    if(element == null) {
     412        return "Error";
     413    }
     414    // Get namespace for given element.
     415    Element root = (Element)element.getParentNode();
     416    if(root != null) {
     417        String identifier = root.getAttribute("namespace");
    418418                // Get name and return it.
    419                 if(identifier.equals("")) {
    420                      identifier = Utility.EXTRACTED_METADATA_NAMESPACE;
    421                 }
    422                 return identifier + NS_SEP + getIdentifier(element);
    423           }
    424           return getIdentifier(element); // No namespace anymore
    425     } // static public String getFullIdentier(Node element)
    426 
    427     /*************************************************************************/
    428     /** Method to construct an elements fully qualified name. Note that this is different from a nodes identifier. Think of name as a short, unique reference to an metadata element, whereas identifier can be much longer, language specific and non-unique.
    429       * @param element An <strong>Element</strong> whose name we are interested in.
    430       * @return A <strong>String</strong> representing this given elements fully namespace qualified name.
    431       */
    432     static final public String getFullName(Element element) {
    433           if(element == null) {
    434                 return null;
    435           }
    436           // Get namespace for given element.
    437           Element root = (Element)element.getParentNode();
    438           String identifier = root.getAttribute("namespace") + NS_SEP + element.getAttribute("name");
    439           // Get name and return it.
    440           if(identifier.startsWith(".")) {
    441                 identifier = identifier.substring(1);
    442           }
    443           return identifier;
    444     } // static public String getFullName(Element element)
    445 
    446     /** Method to construct an elements identifier by retrieving the correct attribute. Language specific, based on default Locale.
    447       * @param element The <strong>Element</strong> whose name we wish to retrieve.
    448       * @return A <strong>String</strong> which is the elements identifier, or an empty string if no identifier exists.
    449       */
    450     static final public String getIdentifier(Node element) {
    451           String identifier = null;
    452           // Determine locale code.
    453           String language_code = Locale.getDefault().getLanguage();
    454           // Get the 'identifier' Element with the correct locale
    455           for(Node node = element.getFirstChild(); node != null;
    456                 node = node.getNextSibling()) {
    457                 if(node.getNodeName().equals("Attribute")) {
    458                      Element target = (Element)node;
    459                      if(target.getAttribute("name").equals("identifier")) {
    460                           Node text = target.getFirstChild();
    461                           if(target.getAttribute("language").equalsIgnoreCase(language_code)) {
    462                                 return text.getNodeValue();
    463                           }
    464                           else if(target.getAttribute("language").equalsIgnoreCase("en")) {
    465                                 identifier = text.getNodeValue();
    466                           }
    467                           else if(identifier == null) {
    468                                 identifier = text.getNodeValue();
    469                           }
    470                           text = null;
    471                      }
    472                      target = null;
    473                 }
    474           }
    475           language_code = null;
    476           // We may have harvested some identifier from the file.
    477           if(identifier != null) {
    478                 return identifier;
    479           }
    480           // Failing the above we return the nodes name instead.
    481           return ((Element)element).getAttribute("name");
    482     }
    483 
    484     /** Retrieve the metadata description element from this fileset node.
    485       * @param fileset The fileset in question.
    486       * @return The description node or null if no such node.
    487       */
    488     static final public Node getMetadataDescription(Node fileset) {
    489           // Locate the child node called filename
    490           for(Node child = fileset.getLastChild(); child != null; child = child.getPreviousSibling()) {
    491                 if(child.getNodeName().equalsIgnoreCase("Description")) {
    492                      return child;
    493                 }
    494           }       
    495           return null;
    496     }
    497 
    498     /** Method to retrieve from the node given, a certain child node with the specified name.
    499       * @param parent The <strong>Node</strong> whose children should be searched.
    500       * @param name The required nodes name as a <strong>String</strong>.
    501       * @return The requested <strong>Node</strong> if it is found, <i>null</i> otherwise.
    502       */
    503     static final public Node getNodeFromNamed(Node parent, String name) {
    504           Node child = null;
    505           for(Node i = parent.getFirstChild(); i != null && child == null;
    506                 i = i.getNextSibling()) {
    507                 if(i.getNodeName().equals(name)) {
    508                      child = i;
    509                 }
    510           }
    511           return child;
    512     }
    513     /** Look for the occurances 'field' of the element and return it if found.
    514       * @return An <i>int</i> which matches the number in the occurances attribute of the element, or 0 if no such attribute.
    515       */
    516     static final public int getOccurances(Element element) {
    517           int count = 0;
    518           String number = null;
    519           if((number = element.getAttribute("occurances")) != null) {
    520                 try {
    521                      count = Integer.parseInt(number);
    522                 }
    523                 catch(Exception error) {
    524                      count = 0;
    525                 }
    526           }
    527           return count;
    528     }   
    529     /** This method extracts the option list details, if any, from a certain element and stores them in an array ready to be passed as arguments to the <strong>Dictionary</strong>.
    530       * @param element The <strong>Element</strong> whose option list we wish to view.
    531       * @return A <strong>String[]</strong> containing the details of the option list.
    532       * TODO implement.
    533       * @see org.greenstone.gatherer.Dictionary
    534       */
    535     static final public String[] getOptionListDetails(Element element) {
    536           return null;
    537     }
    538 
    539     /** This method traces the path between the 'root' node given and the target node. This path is returned starting at the root.
    540       * @param root The root <strong>Node</strong> to start this path at.
    541       * @param target The final <strong>Node</strong> to end up at.
    542       * @return A <strong>Node[]</strong> that contains the sequence of nodes from root to target.
    543       */
    544     static final public Node[] getPath(Node root, Node target) {
    545           if(root == target) {
    546                 return ArrayTools.add(null, root);
    547           }
    548           else {
    549                 return ArrayTools.add(getPath(root, target.getParentNode()), target);
    550           }
    551     }
    552 
    553     /** This method extracts the structural details from a certain element and stores them in an array, all ready for passing to the <strong>Dictionary</strong>.
    554       * @param element The <Strong>Element</strong> whose details we wish to gather.
    555       * @return A <strong>String[]</strong> containing the structural details.
    556       */
    557     static final public String[] getStructuralDetails(Element element) {
    558           String details[] = new String[4];
    559           Element root = (Element)element.getParentNode();
    560           details[0] = root.getAttribute("name");
    561           details[1] = root.getAttribute("namespace");
    562           details[2] = getFullName(element);
    563           details[3] = null;
    564           // Get attributes
    565           Vector attributes = new Vector();
    566           for(Node n=element.getFirstChild(); n!=null; n=n.getNextSibling()) {
    567                 if(n.getNodeName().equals("Attribute")) {
    568                      Element temp = (Element)n;
    569                      attributes.add(temp.getAttribute("name") + "=" + getValue(n));
    570                 }
    571           }
    572           // Sort attributes
    573           Collections.sort(attributes);
    574           // Add attributes to details.
    575           for(int i = 0; i < attributes.size(); i++) {
    576                 if(details[3] == null) {
    577                      details[3] = "   " + attributes.get(i);
    578                 }
    579                 else {
    580                      details[3] = details[3] + "\n   " + attributes.get(i);
    581                 }
    582           }
    583           return details;
    584     }
    585 
    586     /** Method to retrieve the value of a given node (not the assigned values tree!).
    587       * @param element The <strong>Element</strong> whose value we wish to find.
    588       * @return The value found as a <strong>String</strong>, or <i>null</i> if this element has no value.
    589       */
    590     static final public String getValue(Node element) {
    591           // If we've been given a subject node first retrieve its value node.
    592           if(element.getNodeName().equals("Subject")) {
    593                 element = getNodeFromNamed(element, "Value");
    594           }
    595           if(element != null && element.hasChildNodes()) {
    596                 Node text = element.getFirstChild();
    597                 return text.getNodeValue();
    598           }
    599           return "";
    600     }
    601 
    602     /** Method to traverse the given value tree, and build up a hashtable of mappings between the value path key names and the Subject nodes of the tree.
    603       * @param current The root <strong>Node</strong> of a subtree of the AssignedValues tree.
    604       * @param prefix The value path key <strong>String</string>, which shows the path from the root of the AssignedValue tree to <i>current</i>s parent using '\' as a separator.
    605       * @param values A <strong>Hashtable</strong> containing the mapping discovered so far in our tree traversal.
    606       */
    607     static final public void getValueMappings(Node current, String prefix, Hashtable values) {
    608           if(current != null) {
    609                 String name = current.getNodeName();
    610                 String new_prefix = prefix;
     419        if(identifier.equals("")) {
     420        identifier = Utility.EXTRACTED_METADATA_NAMESPACE;
     421        }
     422        return identifier + NS_SEP + getIdentifier(element);
     423    }
     424    return getIdentifier(element); // No namespace anymore
     425    } // static public String getFullIdentier(Node element)
     426
     427    /*************************************************************************/
     428    /** Method to construct an elements fully qualified name. Note that this is different from a nodes identifier. Think of name as a short, unique reference to an metadata element, whereas identifier can be much longer, language specific and non-unique.
     429     * @param element An <strong>Element</strong> whose name we are interested in.
     430     * @return A <strong>String</strong> representing this given elements fully namespace qualified name.
     431     */
     432    static final public String getFullName(Element element) {
     433    if(element == null) {
     434        return null;
     435    }
     436    // Get namespace for given element.
     437    Element root = (Element)element.getParentNode();
     438    String identifier = root.getAttribute("namespace") + NS_SEP + element.getAttribute("name");
     439    // Get name and return it.
     440    if(identifier.startsWith(".")) {
     441        identifier = identifier.substring(1);
     442    }
     443    return identifier;
     444    } // static public String getFullName(Element element)
     445
     446    /** Method to construct an elements identifier by retrieving the correct attribute. Language specific, based on default Locale.
     447     * @param element The <strong>Element</strong> whose name we wish to retrieve.
     448     * @return A <strong>String</strong> which is the elements identifier, or an empty string if no identifier exists.
     449     */
     450    static final public String getIdentifier(Node element) {
     451    String identifier = null;
     452    // Determine locale code.
     453    String language_code = Locale.getDefault().getLanguage();
     454    // Get the 'identifier' Element with the correct locale
     455    for(Node node = element.getFirstChild(); node != null;
     456        node = node.getNextSibling()) {
     457        if(node.getNodeName().equals("Attribute")) {
     458        Element target = (Element)node;
     459        if(target.getAttribute("name").equals("identifier")) {
     460            Node text = target.getFirstChild();
     461            if(target.getAttribute("language").equalsIgnoreCase(language_code)) {
     462            return text.getNodeValue();
     463            }
     464            else if(target.getAttribute("language").equalsIgnoreCase("en")) {
     465            identifier = text.getNodeValue();
     466            }
     467            else if(identifier == null) {
     468            identifier = text.getNodeValue();
     469            }
     470            text = null;
     471        }
     472        target = null;
     473        }
     474    }
     475    language_code = null;
     476    // We may have harvested some identifier from the file.
     477    if(identifier != null) {
     478        return identifier;
     479    }
     480    // Failing the above we return the nodes name instead.
     481    return ((Element)element).getAttribute("name");
     482    }
     483
     484    /** Retrieve the metadata description element from this fileset node.
     485     * @param fileset The fileset in question.
     486     * @return The description node or null if no such node.
     487     */
     488    static final public Node getMetadataDescription(Node fileset) {
     489    // Locate the child node called filename
     490    for(Node child = fileset.getLastChild(); child != null; child = child.getPreviousSibling()) {
     491        if(child.getNodeName().equalsIgnoreCase("Description")) {
     492        return child;
     493        }
     494    }         
     495    return null;
     496    }
     497
     498    /** Method to retrieve from the node given, a certain child node with the specified name.
     499     * @param parent The <strong>Node</strong> whose children should be searched.
     500     * @param name The required nodes name as a <strong>String</strong>.
     501     * @return The requested <strong>Node</strong> if it is found, <i>null</i> otherwise.
     502     */
     503    static final public Node getNodeFromNamed(Node parent, String name) {
     504    Node child = null;
     505    for(Node i = parent.getFirstChild(); i != null && child == null;
     506        i = i.getNextSibling()) {
     507        if(i.getNodeName().equals(name)) {
     508        child = i;
     509        }
     510    }
     511    return child;
     512    }
     513    /** Look for the occurances 'field' of the element and return it if found.
     514     * @return An <i>int</i> which matches the number in the occurances attribute of the element, or 0 if no such attribute.
     515     */
     516    static final public int getOccurances(Element element) {
     517    int count = 0;
     518    String number = null;
     519    if((number = element.getAttribute("occurances")) != null) {
     520        try {
     521        count = Integer.parseInt(number);
     522        }
     523        catch(Exception error) {
     524        count = 0;
     525        }
     526    }
     527    return count;
     528    }   
     529    /** This method extracts the option list details, if any, from a certain element and stores them in an array ready to be passed as arguments to the <strong>Dictionary</strong>.
     530     * @param element The <strong>Element</strong> whose option list we wish to view.
     531     * @return A <strong>String[]</strong> containing the details of the option list.
     532     * TODO implement.
     533     * @see org.greenstone.gatherer.Dictionary
     534     */
     535    static final public String[] getOptionListDetails(Element element) {
     536    return null;
     537    }
     538
     539    /** This method traces the path between the 'root' node given and the target node. This path is returned starting at the root.
     540     * @param root The root <strong>Node</strong> to start this path at.
     541     * @param target The final <strong>Node</strong> to end up at.
     542     * @return A <strong>Node[]</strong> that contains the sequence of nodes from root to target.
     543     */
     544    static final public Node[] getPath(Node root, Node target) {
     545    if(root == target) {
     546        return ArrayTools.add(null, root);
     547    }
     548    else {
     549        return ArrayTools.add(getPath(root, target.getParentNode()), target);
     550    }
     551    }
     552
     553    /** This method extracts the structural details from a certain element and stores them in an array, all ready for passing to the <strong>Dictionary</strong>.
     554     * @param element The <Strong>Element</strong> whose details we wish to gather.
     555     * @return A <strong>String[]</strong> containing the structural details.
     556     */
     557    static final public String[] getStructuralDetails(Element element) {
     558    String details[] = new String[4];
     559    Element root = (Element)element.getParentNode();
     560    details[0] = root.getAttribute("name");
     561    details[1] = root.getAttribute("namespace");
     562    details[2] = getFullName(element);
     563    details[3] = null;
     564    // Get attributes
     565    Vector attributes = new Vector();
     566    for(Node n=element.getFirstChild(); n!=null; n=n.getNextSibling()) {
     567        if(n.getNodeName().equals("Attribute")) {
     568        Element temp = (Element)n;
     569        attributes.add(temp.getAttribute("name") + "=" + getValue(n));
     570        }
     571    }
     572    // Sort attributes
     573    Collections.sort(attributes);
     574    // Add attributes to details.
     575    for(int i = 0; i < attributes.size(); i++) {
     576        if(details[3] == null) {
     577        details[3] = "   " + attributes.get(i);
     578        }
     579        else {
     580        details[3] = details[3] + "\n   " + attributes.get(i);
     581        }
     582    }
     583    return details;
     584    }
     585
     586    /** Method to retrieve the value of a given node (not the assigned values tree!).
     587     * @param element The <strong>Element</strong> whose value we wish to find.
     588     * @return The value found as a <strong>String</strong>, or <i>null</i> if this element has no value.
     589     */
     590    static final public String getValue(Node element) {
     591    // If we've been given a subject node first retrieve its value node.
     592    if(element.getNodeName().equals("Subject")) {
     593        element = getNodeFromNamed(element, "Value");
     594    }
     595    if(element != null && element.hasChildNodes()) {
     596        Node text = element.getFirstChild();
     597        return text.getNodeValue();
     598    }
     599    return "";
     600    }
     601
     602    /** Method to traverse the given value tree, and build up a hashtable of mappings between the value path key names and the Subject nodes of the tree.
     603     * @param current The root <strong>Node</strong> of a subtree of the AssignedValues tree.
     604     * @param prefix The value path key <strong>String</string>, which shows the path from the root of the AssignedValue tree to <i>current</i>s parent using '\' as a separator.
     605     * @param values A <strong>Hashtable</strong> containing the mapping discovered so far in our tree traversal.
     606     */
     607    static final public void getValueMappings(Node current, String prefix, Hashtable values) {
     608    if(current != null) {
     609        String name = current.getNodeName();
     610        String new_prefix = prefix;
    611611                // If we've found the outer layer of a new value, add it to our
    612612                // mapping.
    613                 if(name.equals("Subject")) {
    614                      Node value_node = getNodeFromNamed(current, "Value");
    615                      String value = getValue(value_node);
    616                      if(new_prefix != null) {
    617                           new_prefix = new_prefix + "\\" + value;
    618                      }
    619                      else {
    620                           new_prefix = value;
    621                      }
    622                      values.put(new_prefix, current);
    623                 }
    624                 if(name.equals("Subject") || name.equals("AssignedValues")) {
    625                      for(Node child = current.getFirstChild(); child != null;
    626                           child = child.getNextSibling()) {
    627                           getValueMappings(child, new_prefix, values);
    628                      }
    629                 }
    630           }
    631     }
    632     /** Parses the value tree template file.
    633       * @return The Document parsed.
    634       */
    635     static final public Document getValueTreeTemplate() {
    636           return Utility.parse(Utility.METADATA_VALUE_TEMPLATE, true);
    637     }
    638     /** Method to compare two OptionsLists for equality.
    639       * @param al A <strong>Node</strong> which represents an OptionList.
    640       * @param bl The <strong>Node</strong> we wish to test against.
    641       * @return A <i>boolean</i> which is <i>true</i> if the two option lists are equal, <i>false</i> otherwise.
    642       * TODO Implementation
    643       */
    644     static final public boolean optionListsEqual(Node al, Node bl) {
    645           // Compare the 'restricted' attribute of the two lists.
    646           Element ae = (Element) al;
    647           Element be = (Element) bl;
    648           if(!ae.getAttribute("restricted").equals
    649               (be.getAttribute("restricted"))) {
    650                 return false;
    651           }
    652           // Compare the Values under each list.
    653           for(Node an = al.getFirstChild(); an != null;
    654                 an = an.getNextSibling()){
    655                 if(an.getNodeName().equals("Value")) {
    656                      boolean matched = false;
    657                      for(Node bn = bl.getFirstChild(); bn != null && !matched;
    658                           bn = bn.getNextSibling()) {
    659                           if(bn.getNodeName().equals("Value")) {
    660                                 matched = valuesEqual(an, bn);
    661                           }
    662                      }
    663                      if(!matched) {
    664                           return false;
    665                      }
    666                 }
    667           }
    668           return true;
    669     }
    670 
    671     static final public void setIdentifier(Node element, String value) {
    672           // Get the 'identifier' Element
    673           for(Node node = element.getFirstChild(); node != null;
    674                 node = node.getNextSibling()) {
    675                 if(node.getNodeName().equals("Attribute")) {
    676                      Element target = (Element)node;
    677                      if(target.getAttribute("name").equals("identifier")) {
    678                           Node text = target.getFirstChild();
    679                           text.setNodeValue(value);
    680                      }
    681                 }
    682           }
    683     }
    684     /** Set the value of the element attribute occurances.
    685       * @param element The <strong>Element</strong> to change.
    686       * @param value The value to change by as an <i>int</i>.
    687       */
    688     static final public void setOccurance(Element element, int value) {
    689           Integer new_value = new Integer(getOccurances(element) + value);
    690           element.setAttribute("occurances", new_value.toString());
    691     }
    692     /** This method also traverses the tree, but this one is used to gather all the values and aliases at once, and to 'prune' the tree if necessary.
    693       * @param current The current root <strong>Node</strong> of this AssignedValues tree subtree.
    694       * @param return_filter This <i>int</i> specifies what nodes from the tree should be returned, where;<br>VALUES  = return values only<br>ALIASES = return aliases only<br>BOTH    = return both values and aliases<br>NONE    = return nothing.
    695       * @param remove_leaves A leaf node is a subject that contains no child subjects. If this <i>boolean</i> is set to <i>true</i> then the leaf nodes will be removed from the tree.
    696       * @return A <strong>Node[]</strong> containing whatever values or aliases have been found during the trees traversal.
    697       */
    698     static final public Node[] traverseTree(Node current, int return_filter, boolean remove_leaves) {
    699           Node leaves[] = null;
    700           String name = current.getNodeName();
    701           if(name.equals("Value") && (return_filter == VALUES || return_filter == BOTH)) {
    702                 leaves = ArrayTools.add(leaves, current);
    703           }
    704           else if(name.equals("Alias") && (return_filter == ALIASES || return_filter == BOTH)) {
    705                 leaves = ArrayTools.add(leaves, current);
    706           }
    707           else if(name.equals("Subject")) {
    708                 boolean has_subject_child = false;
    709                 Node children[] = ArrayTools.nodeListToNodeArray(current.getChildNodes());
    710                 for(int i = 0; i < children.length; i++) {
    711                      if(children[i].getNodeName().equals("Subject")) {
    712                           has_subject_child = true;
    713                      }
    714                      leaves = ArrayTools.add(leaves, traverseTree(children[i], return_filter, remove_leaves));
    715                 }
    716                 if(!has_subject_child && remove_leaves) {
    717                      Node parent = current.getParentNode();
    718                      parent.removeChild(current);
    719                 }
    720           }
    721           else if(name.equals("AssignedValues")) {
    722                 Node children[] = ArrayTools.nodeListToNodeArray(current.getChildNodes());
    723                 for(int i = 0; i < children.length; i++) {
    724                      leaves = ArrayTools.add(leaves, traverseTree(children[i], return_filter, remove_leaves));
    725                 }
    726           }                                                           
    727           return leaves;
    728     }
    729 
    730     /** This method is used to systematically merge two AssignedValues tree. Both trees have their current values mapped, then the new tree is searched for key paths that don't exist in the current tree. If such a key is found, the Subject <strong>Node</strong> it maps to is retrieved and then imported and added to whatever was the closest available node (in terms of tree path) in the current tree.
    731       * @param a_set The MetadataSet from which the Element a came from.
    732       * @param a The Element at the root of the current AssignedValues tree.
    733       * @param b_set The MetadataSet from which the Element b came from.
    734       * @param b The root Element of the tree that is being merged.
    735       * @return A <i>boolean</i> which is <i>true</i> if the trees merged without error, <i>false</i> otherwise.
    736       */
    737     static final public boolean updateValueTree(MetadataSet a_set, Element a, MetadataSet b_set, Element b) {
    738           GValueModel avt = a_set.getValueTree(new ElementWrapper(a));
    739           GValueModel bvt = b_set.getValueTree(new ElementWrapper(b));
    740           // If neither element even has a value tree, we're all done.
    741           if(avt == null && bvt == null) {
    742                 avt = null;
    743                 bvt = null;
    744                 return true;
    745           }
    746           // If the new element has no value tree then nothing needs to be done.
    747           else if(avt != null && bvt == null) {
    748                 avt = null;
    749                 bvt = null;
    750                 return true;
    751           }
    752           // If only the new element has a value tree, then add all of its values
    753           // immediately.
    754           else if(avt == null && bvt != null) {
    755                 a_set.addValueTree(new ElementWrapper(a), bvt);
    756                 avt = null;
    757                 bvt = null;
    758                 return true;
    759           }
    760           // We have both trees for both elements, time to merge.
    761           else {
    762                 Document document = avt.getDocument();
    763                 Hashtable a_map = new Hashtable();
    764                 getValueMappings(document.getDocumentElement(), null, a_map);
    765                 Hashtable b_map = new Hashtable();
    766                 getValueMappings(bvt.getDocument().getDocumentElement(), null, b_map);
     613        if(name.equals("Subject")) {
     614        Node value_node = getNodeFromNamed(current, "Value");
     615        String value = getValue(value_node);
     616        if(new_prefix != null) {
     617            new_prefix = new_prefix + "\\" + value;
     618        }
     619        else {
     620            new_prefix = value;
     621        }
     622        values.put(new_prefix, current);
     623        }
     624        if(name.equals("Subject") || name.equals("AssignedValues")) {
     625        for(Node child = current.getFirstChild(); child != null;
     626            child = child.getNextSibling()) {
     627            getValueMappings(child, new_prefix, values);
     628        }
     629        }
     630    }
     631    }
     632    /** Parses the value tree template file.
     633     * @return The Document parsed.
     634     */
     635    static final public Document getValueTreeTemplate() {
     636    return Utility.parse(Utility.METADATA_VALUE_TEMPLATE, true);
     637    }
     638    /** Method to compare two OptionsLists for equality.
     639     * @param al A <strong>Node</strong> which represents an OptionList.
     640     * @param bl The <strong>Node</strong> we wish to test against.
     641     * @return A <i>boolean</i> which is <i>true</i> if the two option lists are equal, <i>false</i> otherwise.
     642     * TODO Implementation
     643     */
     644    static final public boolean optionListsEqual(Node al, Node bl) {
     645    // Compare the 'restricted' attribute of the two lists.
     646    Element ae = (Element) al;
     647    Element be = (Element) bl;
     648    if(!ae.getAttribute("restricted").equals
     649       (be.getAttribute("restricted"))) {
     650        return false;
     651    }
     652    // Compare the Values under each list.
     653    for(Node an = al.getFirstChild(); an != null;
     654        an = an.getNextSibling()){
     655        if(an.getNodeName().equals("Value")) {
     656        boolean matched = false;
     657        for(Node bn = bl.getFirstChild(); bn != null && !matched;
     658            bn = bn.getNextSibling()) {
     659            if(bn.getNodeName().equals("Value")) {
     660            matched = valuesEqual(an, bn);
     661            }
     662        }
     663        if(!matched) {
     664            return false;
     665        }
     666        }
     667    }
     668    return true;
     669    }
     670
     671    static final public void setIdentifier(Node element, String value) {
     672    // Get the 'identifier' Element
     673    for(Node node = element.getFirstChild(); node != null;
     674        node = node.getNextSibling()) {
     675        if(node.getNodeName().equals("Attribute")) {
     676        Element target = (Element)node;
     677        if(target.getAttribute("name").equals("identifier")) {
     678            Node text = target.getFirstChild();
     679            text.setNodeValue(value);
     680        }
     681        }
     682    }
     683    }
     684    /** Set the value of the element attribute occurances.
     685     * @param element The <strong>Element</strong> to change.
     686     * @param value The value to change by as an <i>int</i>.
     687     */
     688    static final public void setOccurance(Element element, int value) {
     689    Integer new_value = new Integer(getOccurances(element) + value);
     690    element.setAttribute("occurances", new_value.toString());
     691    }
     692    /** This method also traverses the tree, but this one is used to gather all the values and aliases at once, and to 'prune' the tree if necessary.
     693     * @param current The current root <strong>Node</strong> of this AssignedValues tree subtree.
     694     * @param return_filter This <i>int</i> specifies what nodes from the tree should be returned, where;<br>VALUES  = return values only<br>ALIASES = return aliases only<br>BOTH    = return both values and aliases<br>NONE    = return nothing.
     695     * @param remove_leaves A leaf node is a subject that contains no child subjects. If this <i>boolean</i> is set to <i>true</i> then the leaf nodes will be removed from the tree.
     696     * @return A <strong>Node[]</strong> containing whatever values or aliases have been found during the trees traversal.
     697     */
     698    static final public Node[] traverseTree(Node current, int return_filter, boolean remove_leaves) {
     699    Node leaves[] = null;
     700    String name = current.getNodeName();
     701    if(name.equals("Value") && (return_filter == VALUES || return_filter == BOTH)) {
     702        leaves = ArrayTools.add(leaves, current);
     703    }
     704    else if(name.equals("Alias") && (return_filter == ALIASES || return_filter == BOTH)) {
     705        leaves = ArrayTools.add(leaves, current);
     706    }
     707    else if(name.equals("Subject")) {
     708        boolean has_subject_child = false;
     709        Node children[] = ArrayTools.nodeListToNodeArray(current.getChildNodes());
     710        for(int i = 0; i < children.length; i++) {
     711        if(children[i].getNodeName().equals("Subject")) {
     712            has_subject_child = true;
     713        }
     714        leaves = ArrayTools.add(leaves, traverseTree(children[i], return_filter, remove_leaves));
     715        }
     716        if(!has_subject_child && remove_leaves) {
     717        Node parent = current.getParentNode();
     718        parent.removeChild(current);
     719        }
     720    }
     721    else if(name.equals("AssignedValues")) {
     722        Node children[] = ArrayTools.nodeListToNodeArray(current.getChildNodes());
     723        for(int i = 0; i < children.length; i++) {
     724        leaves = ArrayTools.add(leaves, traverseTree(children[i], return_filter, remove_leaves));
     725        }
     726    }                                                             
     727    return leaves;
     728    }
     729
     730    /** This method is used to systematically merge two AssignedValues tree. Both trees have their current values mapped, then the new tree is searched for key paths that don't exist in the current tree. If such a key is found, the Subject <strong>Node</strong> it maps to is retrieved and then imported and added to whatever was the closest available node (in terms of tree path) in the current tree.
     731     * @param a_set The MetadataSet from which the Element a came from.
     732     * @param a The Element at the root of the current AssignedValues tree.
     733     * @param b_set The MetadataSet from which the Element b came from.
     734     * @param b The root Element of the tree that is being merged.
     735     * @return A <i>boolean</i> which is <i>true</i> if the trees merged without error, <i>false</i> otherwise.
     736     */
     737    static final public boolean updateValueTree(MetadataSet a_set, Element a, MetadataSet b_set, Element b) {
     738    GValueModel avt = a_set.getValueTree(new ElementWrapper(a));
     739    GValueModel bvt = b_set.getValueTree(new ElementWrapper(b));
     740    // If neither element even has a value tree, we're all done.
     741    if(avt == null && bvt == null) {
     742        avt = null;
     743        bvt = null;
     744        return true;
     745    }
     746    // If the new element has no value tree then nothing needs to be done.
     747    else if(avt != null && bvt == null) {
     748        avt = null;
     749        bvt = null;
     750        return true;
     751    }
     752    // If only the new element has a value tree, then add all of its values
     753    // immediately.
     754    else if(avt == null && bvt != null) {
     755        a_set.addValueTree(new ElementWrapper(a), bvt);
     756        avt = null;
     757        bvt = null;
     758        return true;
     759    }
     760    // We have both trees for both elements, time to merge.
     761    else {
     762        Document document = avt.getDocument();
     763        Hashtable a_map = new Hashtable();
     764        getValueMappings(document.getDocumentElement(), null, a_map);
     765        Hashtable b_map = new Hashtable();
     766        getValueMappings(bvt.getDocument().getDocumentElement(), null, b_map);
    767767                // For each new entry in b_map
    768                 for(Enumeration b_keys = b_map.keys(); b_keys.hasMoreElements(); ) {
    769                      String b_key = (String)b_keys.nextElement();
    770                      // Test if there is already an entry in a_map.
    771                      if(!a_map.containsKey(b_key)) {
    772                           // If not, search through a_map for the longest match.
    773                           Node target = document.getDocumentElement();
    774                           String last_match = null;
    775                           for(Enumeration a_keys = a_map.keys();
    776                                 a_keys.hasMoreElements(); ) {
    777                                 String a_key = (String)a_keys.nextElement();
    778                                 if(b_key.startsWith(a_key)) {
    779                                     if(last_match == null || a_key.length() > last_match.length()) {
    780                                           last_match = a_key;
    781                                           target = (Node)a_map.get(a_key);
    782                                     }
    783                                 }
    784                                 a_key = null;
    785                           }
    786                           // Now import the node at b_key and add it to target.
    787                           Node subtree = (Node)b_map.get(b_key);
    788                           subtree = document.importNode(subtree, true);
    789                           // Find the node to insert before...
    790                           String name = getValue(subtree);
    791                           Node move = null;
    792                           for(Node n = target.getFirstChild(); n != null && move == null; n = n.getNextSibling()) {
    793                                 if(n.getNodeName().equals("Subject")) {
    794                                     if(name.compareTo(getValue(n)) <= 0) {
    795                                           move = n;
    796                                     }
    797                                 }
    798                           }
    799                           if(move == null) {
    800                                 target.appendChild(subtree);
    801                           }
    802                           else {
    803                                 target.insertBefore(subtree, move);
    804                           }
    805                           target = null;
    806                           last_match = null;
    807                           subtree = null;
    808                           name = null;
    809                           move = null;
    810                      }
    811                      b_key = null;
    812                 }
    813                 document = null;
    814                 a_map = null;
    815                 b_map = null;
    816                 avt = null;
    817                 bvt = null;
    818                 return true;
    819           }
    820     }
    821 
    822     /** Method to determine if two Value nodes are equal.
    823       * @param av A <strong>Node</strong> representing a value.
    824       * @param bv The <strong>Node</strong> we want to compare it to.
    825       * @return A <i>boolean</i> which is <i>true</i> if the two value nodes are equal, <i>false</i> otherwise.
    826       */
    827     static final public boolean valuesEqual(Node av, Node bv) {
    828           // Check we are comparing apples and apples...
    829           if(av.getNodeName().equals("Value") &&
    830               bv.getNodeName().equals("Value")) {
     768        for(Enumeration b_keys = b_map.keys(); b_keys.hasMoreElements(); ) {
     769        String b_key = (String)b_keys.nextElement();
     770        // Test if there is already an entry in a_map.
     771        if(!a_map.containsKey(b_key)) {
     772            // If not, search through a_map for the longest match.
     773            Node target = document.getDocumentElement();
     774            String last_match = null;
     775            for(Enumeration a_keys = a_map.keys();
     776            a_keys.hasMoreElements(); ) {
     777            String a_key = (String)a_keys.nextElement();
     778            if(b_key.startsWith(a_key)) {
     779                if(last_match == null || a_key.length() > last_match.length()) {
     780                last_match = a_key;
     781                target = (Node)a_map.get(a_key);
     782                }
     783            }
     784            a_key = null;
     785            }
     786            // Now import the node at b_key and add it to target.
     787            Node subtree = (Node)b_map.get(b_key);
     788            subtree = document.importNode(subtree, true);
     789            // Find the node to insert before...
     790            String name = getValue(subtree);
     791            Node move = null;
     792            for(Node n = target.getFirstChild(); n != null && move == null; n = n.getNextSibling()) {
     793            if(n.getNodeName().equals("Subject")) {
     794                if(name.compareTo(getValue(n)) <= 0) {
     795                move = n;
     796                }
     797            }
     798            }
     799            if(move == null) {
     800            target.appendChild(subtree);
     801            }
     802            else {
     803            target.insertBefore(subtree, move);
     804            }
     805            target = null;
     806            last_match = null;
     807            subtree = null;
     808            name = null;
     809            move = null;
     810        }
     811        b_key = null;
     812        }
     813        document = null;
     814        a_map = null;
     815        b_map = null;
     816        avt = null;
     817        bvt = null;
     818        return true;
     819    }
     820    }
     821
     822    /** Method to determine if two Value nodes are equal.
     823     * @param av A <strong>Node</strong> representing a value.
     824     * @param bv The <strong>Node</strong> we want to compare it to.
     825     * @return A <i>boolean</i> which is <i>true</i> if the two value nodes are equal, <i>false</i> otherwise.
     826     */
     827    static final public boolean valuesEqual(Node av, Node bv) {
     828    // Check we are comparing apples and apples...
     829    if(av.getNodeName().equals("Value") &&
     830       bv.getNodeName().equals("Value")) {
    831831                // Retrieve and then compare their text values.
    832                 Node at = av.getFirstChild();
    833                 Node bt = bv.getFirstChild();
    834                 if(at.getNodeValue().equals(bt.getNodeValue())) {
    835                      return true;
    836                 }
    837           }
    838           return false;
    839     }
    840 
    841     /** A comparator for sorting metadata element-value pairs into their standard order (elements) then alphabetical order (values). */
    842     static final private class MetadataComparator
    843           implements Comparator {
    844           /** Compares its two arguments for order. */
    845           public int compare(Object o1, Object o2) {
    846                 int result = 0;
    847                 Metadata m1 = (Metadata) o1;
    848                 Metadata m2 = (Metadata) o2;
     832        Node at = av.getFirstChild();
     833        Node bt = bv.getFirstChild();
     834        if(at.getNodeValue().equals(bt.getNodeValue())) {
     835        return true;
     836        }
     837    }
     838    return false;
     839    }
     840
     841    /** A comparator for sorting metadata element-value pairs into their standard order (elements) then alphabetical order (values). */
     842    static final private class MetadataComparator
     843    implements Comparator {
     844    /** Compares its two arguments for order. */
     845    public int compare(Object o1, Object o2) {
     846        int result = 0;
     847        Metadata m1 = (Metadata) o1;
     848        Metadata m2 = (Metadata) o2;
    849849                ///ystem.err.println("MSMUtils.compare(" + m1 + ", " + m2 + ") = ");
    850                 ElementWrapper e1 = m1.getElement();
    851                 ElementWrapper e2 = m2.getElement();
     850        ElementWrapper e1 = m1.getElement();
     851        ElementWrapper e2 = m2.getElement();
    852852                // First we compare the namespaces
    853                 result = e1.getNamespace().compareTo(e2.getNamespace());
    854                 if(result == 0) {
    855                      // Now, given both elements are in the same set, we compare the element ordering using methods in MetadataSet
    856                      MetadataSet set = Gatherer.c_man.getCollection().msm.getSet(e1.getNamespace());
    857                      ///ystem.err.print("MetadataSet.compare(" + e1 + ", " + e2 + ") = ");
    858                      result = set.compare(e1.getElement(), e2.getElement());
    859                      ///ystem.err.println(result);
    860                      if(result == 0) {
    861                           // Finally we compare the values alphabetically.
    862                           result = m1.getValue().compareTo(m2.getValue());
    863                      }
    864                 }
     853        result = e1.getNamespace().compareTo(e2.getNamespace());
     854        if(result == 0) {
     855        // Now, given both elements are in the same set, we compare the element ordering using methods in MetadataSet
     856        MetadataSet set = Gatherer.c_man.getCollection().msm.getSet(e1.getNamespace());
     857        ///ystem.err.print("MetadataSet.compare(" + e1 + ", " + e2 + ") = ");
     858        result = set.compare(e1.getElement(), e2.getElement());
     859        ///ystem.err.println(result);
     860        if(result == 0) {
     861            // Finally we compare the values alphabetically.
     862            result = m1.getValue().compareTo(m2.getValue());
     863        }
     864        }
    865865                ///ystem.err.println("Result: " + result);
    866                 return result;
    867           }
     866        return result;
     867    }
    868868         
    869           /** Indicates whether some other object is "equal to" this Comparator. */
    870           public boolean equals(Object obj) {
    871                return compare(this, obj) == 0;
    872           }
    873     }
     869    /** Indicates whether some other object is "equal to" this Comparator. */
     870    public boolean equals(Object obj) {
     871        return compare(this, obj) == 0;
     872    }
     873    }
    874874}
  • trunk/gli/src/org/greenstone/gatherer/msm/Metadata.java

    r4293 r4365  
    5050 */
    5151public class Metadata
    52     implements Comparable {
    53     /** Is this an accumulating piece of metadata? */
    54     transient private boolean accumulate = false;
    55     /** Indicates what level of metadata this is, relative to the file it was retrieved for. */
    56     transient private boolean file_level = true;
    57     /** The file for which this metadata was retrieved. This allows us to determine the source of folder level metadata. */
    58     transient private File file;
    59     /** The value node this metadata maps to. */
    60     private GValueNode value = null;
    61     /** Used to count the number of references to this piece of metadata within the metadata table. */
    62     private int count = 1;
    63     /** The metadata element this metadata maps to. */
    64     private ElementWrapper element = null;
    65     /** Constructs a new Metadata object with no current value.
    66       * @param element The <strong>ElementWrapper</strong> associated with this metadata.
    67       */
    68     public Metadata(ElementWrapper element) {
    69           super();
    70           this.element = element;
    71           this.value = null;
    72     }
    73     /** Default constructor simply creates a new Metadata object based on the given parameters.
     52    implements Comparable {
     53    /** Is this an accumulating piece of metadata? */
     54    transient private boolean accumulate = false;
     55    /** Indicates what level of metadata this is, relative to the file it was retrieved for. */
     56    transient private boolean file_level = true;
     57    /** The file for which this metadata was retrieved. This allows us to determine the source of folder level metadata. */
     58    transient private File file;
     59    /** The value node this metadata maps to. */
     60    private GValueNode value = null;
     61    /** Used to count the number of references to this piece of metadata within the metadata table. */
     62    private int count = 1;
     63    /** The metadata element this metadata maps to. */
     64    private ElementWrapper element = null;
     65    /** Constructs a new Metadata object with no current value.
     66     * @param element The <strong>ElementWrapper</strong> associated with this metadata.
     67      */
     68    public Metadata(ElementWrapper element) {
     69    super();
     70    this.element = element;
     71    this.value = null;
     72    }
     73    /** Default constructor simply creates a new Metadata object based on the given parameters.
    7474      * @param element The <strong>ElementWrapper</strong> associated with this metadata.
    7575      * @param value The assigned value for the given element, as a <strong>GValueNode</strong>.
    7676      */
    77     public Metadata(ElementWrapper element, GValueNode value) {
    78           super();
    79           this.element = element;
    80           this.value = value;
    81     }
    82     /** Constructs a new Metadata object, given the value. The metadata element is extracted from the value tree information.
     77    public Metadata(ElementWrapper element, GValueNode value) {
     78    super();
     79    this.element = element;
     80    this.value = value;
     81    }
     82    /** Constructs a new Metadata object, given the value. The metadata element is extracted from the value tree information.
    8383      * @param value The assigned value for the given element, as a <strong>GValueNode</strong>.
    8484      * @see org.greenstone.gatherer.Gatherer
     
    8686      * @see org.greenstone.gatherer.msm.MetadataSetManager
    8787      */
    88     public Metadata(GValueNode value) {
    89           super();
    90           this.element = Gatherer.c_man.getCollection().msm.getElement(value.getMetadataElementName());
    91           this.value = value;
    92     }
    93     /** Determine if this metadata overwrites or accumulates. */
    94     public boolean accumulates() {
    95           return accumulate;
    96     }
    97     /** Compares two Metadata objects for ordering purposes.
     88    public Metadata(GValueNode value) {
     89    super();
     90    this.element = Gatherer.c_man.getCollection().msm.getElement(value.getMetadataElementName());
     91    this.value = value;
     92    }
     93    /** Determine if this metadata overwrites or accumulates. */
     94    public boolean accumulates() {
     95    return accumulate;
     96    }
     97    /** Compares two Metadata objects for ordering purposes.
    9898      * @param object The other metadata as an <strong>Object</strong>.
    9999      * @return An <i>int</i> value as specified in java.lang.String#compareTo
    100100      * @see java.lang.String#compareTo
    101101      */
    102     public int compareTo(Object object) {
    103           return toString().compareTo(object.toString());
    104     }
    105     /** Decrease the reference count by 1. */
    106     public void dec() {
    107           count--;
    108     }
    109     /** Tests to metadata objects for equality.
     102    public int compareTo(Object object) {
     103    return toString().compareTo(object.toString());
     104    }
     105    /** Decrease the reference count by 1. */
     106    public void dec() {
     107    count--;
     108    }
     109    /** Tests to metadata objects for equality.
    110110      * @param object The other metadata as an <strong>Object</strong>.
    111111      * @return <i>true</i> if the metadata are equal, <i>false</i> otherwise.
    112112      */
    113     public boolean equals(Object object) {
    114           if(compareTo(object) == 0) {
    115                 return true;
    116           }
    117           return false;
    118     }
    119     /** Determine the absolute path value of this metadata, taking into account whether this value is hierarchical. */
    120     public String getAbsoluteValue() {
    121           String abs_value = getValue();
    122           // What actually gets written as the value depends on whether this is a hierarchy based element.
    123           GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(element);
    124           if(model != null && model.isHierarchy()) {
    125                 if(value != null) {
    126                      GValueNode node = (GValueNode)value.getParent();
    127                      while(node != null) {
    128                           GValueNode next = (GValueNode)node.getParent();
    129                           if(next != null) {
    130                                 abs_value = node.toString() + "\\" + abs_value;
    131                           }
    132                           node = next;
    133                      }
    134                 }
    135                 abs_value = model.getHIndex(abs_value);
    136           }
    137           // Return the result
    138           return abs_value;
    139     }
    140     /** Retrieve the reference count.
     113    public boolean equals(Object object) {
     114    if(compareTo(object) == 0) {
     115        return true;
     116    }
     117    return false;
     118    }
     119    /** Determine the absolute path value of this metadata, taking into account whether this value is hierarchical. */
     120    public String getAbsoluteValue() {
     121    String abs_value = getValue();
     122    // What actually gets written as the value depends on whether this is a hierarchy based element.
     123    GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(element);
     124    if(model != null && model.isHierarchy()) {
     125        if(value != null) {
     126        GValueNode node = (GValueNode)value.getParent();
     127        while(node != null) {
     128            GValueNode next = (GValueNode)node.getParent();
     129            if(next != null) {
     130            abs_value = node.toString() + "\\" + abs_value;
     131            }
     132            node = next;
     133        }
     134        }
     135        abs_value = model.getHIndex(abs_value);
     136    }
     137    // Return the result
     138    return abs_value;
     139    }
     140    /** Retrieve the reference count.
    141141      * @return The count as an <i>int</i>.
    142142      */
    143     public int getCount() {
    144           return count;
    145     }
    146     /** Retrieve the element associated with this metadata.
     143    public int getCount() {
     144    return count;
     145    }
     146    /** Retrieve the element associated with this metadata.
    147147      * @return An <strong>ElementWrapper</strong>.
    148148      */
    149     public ElementWrapper getElement() {
    150           return element;
    151     }
    152 
    153     /** Retrieve the source file if any. */
    154     public File getFile() {
    155           return file;
    156     }
    157 
    158     /** Get the textual value of this metadata.
     149    public ElementWrapper getElement() {
     150    return element;
     151    }
     152
     153    /** Retrieve the source file if any. */
     154    public File getFile() {
     155    return file;
     156    }
     157
     158    /** Get the textual value of this metadata.
    159159      * @return A <strong>String</strong> representing the value of this Metadata. Note that if the value node is null, then the String returned is "".
    160160      */
    161     public String getValue() {
    162           String result = "";
    163           if(value != null) {
    164                 result = value.toString();
    165           }
    166           return result;
    167     }
    168     /** Retrieve the value node associated with this metadata.
     161    public String getValue() {
     162    String result = "";
     163    if(value != null) {
     164        result = value.toString();
     165    }
     166    return result;
     167    }
     168    /** Retrieve the value node associated with this metadata.
    169169      * @return A <strong>GValueNode</strong>.
    170170      */
    171     public GValueNode getValueNode() {
    172           return value;
    173     }
    174     /** Increase the reference count by 1. */
    175     public void inc() {
    176           count++;
    177     }
     171    public GValueNode getValueNode() {
     172    return value;
     173    }
     174    /** Increase the reference count by 1. */
     175    public void inc() {
     176    count++;
     177    }
    178178     
    179     /** Determine if this metadata is file level or folder level. */
    180     public boolean isFileLevel() {
    181           return file_level;
    182     }
    183 
    184     /** Inform this metadata whether it will accumulate with any other metadata of the same type. */
    185     public void setAccumulate(boolean accumulate) {
    186           this.accumulate = accumulate;
    187     }
    188     /** Sets the reference count.
     179    /** Determine if this metadata is file level or folder level. */
     180    public boolean isFileLevel() {
     181    return file_level;
     182    }
     183
     184    /** Inform this metadata whether it will accumulate with any other metadata of the same type. */
     185    public void setAccumulate(boolean accumulate) {
     186    this.accumulate = accumulate;
     187    }
     188    /** Sets the reference count.
    189189      * @param value The new value of count as an <i>int</i>.
    190190      */
    191     public void setCount(int value) {
    192           count = value;
    193     }
    194     /** Set the level of this metadata (whether it is associated with this file explicitly or found in some folder above this file). */
    195     public void setFileLevel(boolean file_level) {
    196           this.file_level = file_level;
    197     }
    198 
    199     /** Set the current source file for this metadata to the given collection. This value should not be counted are remaining valid at any time other than immediately after a getMetadata call as it is relative the the file that metadata call was for. */
    200     public void setFile(File file) {
    201           this.file = file;
    202     }
    203 
    204     /** Translates this object into a string representation.
     191    public void setCount(int value) {
     192    count = value;
     193    }
     194    /** Set the level of this metadata (whether it is associated with this file explicitly or found in some folder above this file). */
     195    public void setFileLevel(boolean file_level) {
     196    this.file_level = file_level;
     197    }
     198
     199    /** Set the current source file for this metadata to the given collection. This value should not be counted are remaining valid at any time other than immediately after a getMetadata call as it is relative the the file that metadata call was for. */
     200    public void setFile(File file) {
     201    this.file = file;
     202    }
     203
     204    /** Translates this object into a string representation.
    205205      * @return A <strong>String</strong>.
    206206      */
    207     public String toString() {
    208           return element.toString() + "=" + getValue();
    209     }
    210     /** Retrieve the value node associated with a certain value string. */
    211     static final public GValueNode getDefaultValueNode(ElementWrapper element, String value) {
    212           // Retrieve the GValueNode
    213           GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(element);
    214           GValueNode value_node;
    215           if(model != null) {
    216                 value_node = model.getValue(value);
    217           }
    218           else {
    219                 value_node = new GValueNode(element.getName(), value);
    220           }
    221           model = null;
    222           return value_node;
    223     }
     207    public String toString() {
     208    return element.toString() + "=" + getValue();
     209    }
     210    /** Retrieve the value node associated with a certain value string. */
     211    static final public GValueNode getDefaultValueNode(ElementWrapper element, String value) {
     212    // Retrieve the GValueNode
     213    GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(element);
     214    GValueNode value_node;
     215    if(model != null) {
     216        value_node = model.getValue(value);
     217    }
     218    else {
     219        value_node = new GValueNode(element.getName(), value);
     220    }
     221    model = null;
     222    return value_node;
     223    }
    224224}
  • trunk/gli/src/org/greenstone/gatherer/msm/MetadataComboBoxModel.java

    r4293 r4365  
    5353/** A combobox model that contains information from an array of NodeLists. */
    5454public class MetadataComboBoxModel
    55     extends DefaultComboBoxModel
    56     implements MSMListener {
    57     private MetadataSetManager msm = null;
    58     /** Constructor.
    59       * @param nodelists The <strong>NodeList[]</strong> this model is proxied to.
    60       */
    61     public MetadataComboBoxModel(MetadataSetManager msm) {
    62           super();
    63           this.msm = msm;
    64           init();
    65     }
    66     /** Called whenever a metadata element is changed.
    67       * @param event A <strong>MSMEvent</strong> containing pertinent information about the event.
    68       */
    69     public void elementChanged(MSMEvent event) {
    70           init();
    71     }
    72     /** Called whenever a metadata value is changed.
    73       * @param event A <strong>MSMEvent</strong> containing pertinent information about the event.
    74       */
    75     public void metadataChanged(MSMEvent event) {
    76           // Don't care.
    77     }
    78     /** Called whenever a metadata set is changed.
    79       * @param event A <strong>MSMEvent</strong> containing pertinent information about the event.
    80       */
    81     public void setChanged(MSMEvent event) {
    82           init();
    83     }
    84     /** Called whenever the value tree of a certain element is significantly changed.
    85       * @param event A <strong>MSMEvent</strong> containing information about the event.
    86       */
    87     public void valueChanged(MSMEvent event) {
    88           // Don't care.
    89     }
    90     private void init() {
    91           removeAllElements();
    92           Vector elements = msm.getElements();
    93           Collections.sort(elements);
    94           for(int k = 0; k < elements.size(); k++) {
    95                 addElement(elements.get(k));
    96           }
    97     }
     55    extends DefaultComboBoxModel
     56    implements MSMListener {
     57    private MetadataSetManager msm = null;
     58    /** Constructor.
     59     * @param nodelists The <strong>NodeList[]</strong> this model is proxied to.
     60     */
     61    public MetadataComboBoxModel(MetadataSetManager msm) {
     62    super();
     63    this.msm = msm;
     64    init();
     65    }
     66    /** Called whenever a metadata element is changed.
     67     * @param event A <strong>MSMEvent</strong> containing pertinent information about the event.
     68     */
     69    public void elementChanged(MSMEvent event) {
     70    init();
     71    }
     72    /** Called whenever a metadata value is changed.
     73     * @param event A <strong>MSMEvent</strong> containing pertinent information about the event.
     74     */
     75    public void metadataChanged(MSMEvent event) {
     76    // Don't care.
     77    }
     78    /** Called whenever a metadata set is changed.
     79     * @param event A <strong>MSMEvent</strong> containing pertinent information about the event.
     80     */
     81    public void setChanged(MSMEvent event) {
     82    init();
     83    }
     84    /** Called whenever the value tree of a certain element is significantly changed.
     85     * @param event A <strong>MSMEvent</strong> containing information about the event.
     86     */
     87    public void valueChanged(MSMEvent event) {
     88    // Don't care.
     89    }
     90    private void init() {
     91    removeAllElements();
     92    Vector elements = msm.getElements();
     93    Collections.sort(elements);
     94    for(int k = 0; k < elements.size(); k++) {
     95        addElement(elements.get(k));
     96    }
     97    }
    9898}
    99 
    100 
    101 
    102 
    103 
  • trunk/gli/src/org/greenstone/gatherer/msm/MetadataParser.java

    r4293 r4365  
    4343 */
    4444public interface MetadataParser {
    45     /** Locate and import any metadata parsed by this metadata parser given the file involved and its previous incarnation. The last parameter indicates that the parser is forced to assign otherwise folder level metadata to the desired -files-. If the filenode was a directory folder_level is automatically true.*/
    46     public boolean process(FileNode destination, FileNode origin, boolean folder_level, boolean dummy_run);
     45    /** Locate and import any metadata parsed by this metadata parser given the file involved and its previous incarnation. The last parameter indicates that the parser is forced to assign otherwise folder level metadata to the desired -files-. If the filenode was a directory folder_level is automatically true.*/
     46    public boolean process(FileNode destination, FileNode origin, boolean folder_level, boolean dummy_run);
    4747}
  • trunk/gli/src/org/greenstone/gatherer/msm/MetadataSet.java

    r4331 r4365  
    5050 */
    5151public class MetadataSet {
    52     /** The <Strong>Document</strong> of the DOM model. */
    53     private Document document = null;
    54     /** The document <strong>Element</strong> of the DOM model. */
    55     private Element  root     = null;
    56     /** The <strong>File</strong> this metadata set was loaded from. */
    57     private File     file     = null;
    58     /** A mapping from metadata elements to the root element of the value trees for that element. */
    59     private Hashtable value_trees = null;
    60     /** The list of metadata elements which are, of course, children of the root node. */
    61     private NodeList elements = null;
    62     /** The description of this metadata set. Cached as it takes more computation time. */
    63     private String description = null;
    64     /** The name of this metadata set. Cached as it takes more computation time. */
    65     private String name = null;
    66     /** An element of the tree pruning filter enumeration, that indicates all nodes in the tree should be retained. */
    67     static final int ALL_VALUES    = 1;
    68     /** An element of the tree pruning filter enumeration, that indicates only metadata Subject nodes or higher should remain after pruning. */
    69     static final int SUBJECTS_ONLY = 2;
    70     /** An element of the tree pruning filter enumeration, that indicates no value nodes i.e. the entire AssignedValues subtree, should remain after pruning. */
    71     static final int NO_VALUES     = 3;
    72 
    73     public MetadataSet(String metadata_template) {
    74           URL url = ClassLoader.getSystemResource(metadata_template);
    75           try {
    76                 init(new File(URLDecoder.decode(url.getFile(), "UTF-8")));
    77           }
    78           catch(UnsupportedEncodingException exception) {
    79                 Gatherer.printStackTrace(exception);
    80           }
    81     }
    82 
    83     /** Constructor.
    84       * @param file The file the metadata set should be loaded from.
    85       */
    86     public MetadataSet(File file) {
    87           init(file);
    88     }
    89 
    90     /** Metadata Set already parsed constructor.
    91       * @param file The file the metadata was loaded from.
    92       * @param document The DOM model <strong>Document</strong> containing the metadata set.
    93       */
    94     public MetadataSet(File file, Document document) {
    95           this.document = document;
    96           this.elements = document.getElementsByTagName("Element");
    97           this.file     = file;
    98           this.root     = document.getDocumentElement();
    99           this.value_trees = new Hashtable();
    100           // Now for each element read in its value tree if present.
    101           for(int i = elements.getLength() - 1; i >= 0; i--) {
    102                 ElementWrapper value_element = new ElementWrapper((Element)elements.item(i));
    103                 File value_file = new File(file.getParentFile(), value_element.toString() + ".mdv");
     52    /** The <Strong>Document</strong> of the DOM model. */
     53    private Document document = null;
     54    /** The document <strong>Element</strong> of the DOM model. */
     55    private Element  root     = null;
     56    /** The <strong>File</strong> this metadata set was loaded from. */
     57    private File     file     = null;
     58    /** A mapping from metadata elements to the root element of the value trees for that element. */
     59    private Hashtable value_trees = null;
     60    /** The list of metadata elements which are, of course, children of the root node. */
     61    private NodeList elements = null;
     62    /** The description of this metadata set. Cached as it takes more computation time. */
     63    private String description = null;
     64    /** The name of this metadata set. Cached as it takes more computation time. */
     65    private String name = null;
     66    /** An element of the tree pruning filter enumeration, that indicates all nodes in the tree should be retained. */
     67    static final int ALL_VALUES    = 1;
     68    /** An element of the tree pruning filter enumeration, that indicates only metadata Subject nodes or higher should remain after pruning. */
     69    static final int SUBJECTS_ONLY = 2;
     70    /** An element of the tree pruning filter enumeration, that indicates no value nodes i.e. the entire AssignedValues subtree, should remain after pruning. */
     71    static final int NO_VALUES     = 3;
     72
     73    public MetadataSet(String metadata_template) {
     74    URL url = ClassLoader.getSystemResource(metadata_template);
     75    try {
     76        init(new File(URLDecoder.decode(url.getFile(), "UTF-8")));
     77    }
     78    catch(UnsupportedEncodingException exception) {
     79        Gatherer.printStackTrace(exception);
     80    }
     81    }
     82
     83    /** Constructor.
     84     * @param file The file the metadata set should be loaded from.
     85     */
     86    public MetadataSet(File file) {
     87    init(file);
     88    }
     89
     90    /** Metadata Set already parsed constructor.
     91     * @param file The file the metadata was loaded from.
     92     * @param document The DOM model <strong>Document</strong> containing the metadata set.
     93     */
     94    public MetadataSet(File file, Document document) {
     95    this.document = document;
     96    this.elements = document.getElementsByTagName("Element");
     97    this.file     = file;
     98    this.root     = document.getDocumentElement();
     99    this.value_trees = new Hashtable();
     100    // Now for each element read in its value tree if present.
     101    for(int i = elements.getLength() - 1; i >= 0; i--) {
     102        ElementWrapper value_element = new ElementWrapper((Element)elements.item(i));
     103        File value_file = new File(file.getParentFile(), value_element.toString() + ".mdv");
    104104                ///ystem.err.println("Searching for " + value_file.getAbsolutePath());
    105                 if(value_file.exists()) {
    106                      Document value_document = Utility.parse(value_file, false);
    107                      if(value_document != null) {
    108                           value_trees.put(value_element, new GValueModel(value_element, value_document));
    109                      }
    110                      else {
    111                           Gatherer.println("Error! Missing mdv file: " + value_file.getAbsolutePath());
    112                      }
    113                 }
    114           }
    115     }
    116     /** Copy constructor.
    117       * @param original The original metadata set to copy from.
    118       */
    119     public MetadataSet(MetadataSet original) {
    120           this.value_trees = new Hashtable();
    121           // We have to create a new document.
    122           document = new DocumentImpl(original.getDocument().getDoctype());
    123           root = (Element) document.importNode(original.getDocument().getDocumentElement(), true);
    124           document.appendChild(root);
    125           elements = root.getElementsByTagName("Element");
    126           file = original.getFile();
    127           // Now for each element read in its value tree if present.
    128           for(int i = elements.getLength() - 1; i >= 0; i--) {
    129                 ElementWrapper value_element_wrapper = new ElementWrapper((Element)elements.item(i));
    130                 GValueModel value_tree = original.getValueTree(value_element_wrapper);
    131                 Document value_document = value_tree.getDocument();
    132                 Document value_document_copy = new DocumentImpl(value_document.getDoctype());
    133                 Element value_element = value_document.getDocumentElement();
    134                 Element value_element_copy = (Element) value_document_copy.importNode(value_element, true);
    135                 value_document_copy.appendChild(value_element_copy);
    136                 GValueModel value_tree_copy = new GValueModel(value_element_wrapper, value_document_copy);
    137                 value_trees.put(value_element_wrapper, value_tree_copy);
    138           }
    139     }
    140     /** Conditional copy constructor.
    141       * @param original The original metadata set to copy from.
    142       * @param condition An <i>int</i> which matches one of the tree pruning filter types.
    143       */
    144     public MetadataSet(MetadataSet original, int condition) {
    145           this(original);
    146           // Now based on condition, we may have to remove some nodes from
    147           // this model.
    148           switch(condition) {
    149           case ALL_VALUES:
     105        if(value_file.exists()) {
     106        Document value_document = Utility.parse(value_file, false);
     107        if(value_document != null) {
     108            value_trees.put(value_element, new GValueModel(value_element, value_document));
     109        }
     110        else {
     111            Gatherer.println("Error! Missing mdv file: " + value_file.getAbsolutePath());
     112        }
     113        }
     114    }
     115    }
     116    /** Copy constructor.
     117     * @param original The original metadata set to copy from.
     118     */
     119    public MetadataSet(MetadataSet original) {
     120    this.value_trees = new Hashtable();
     121    // We have to create a new document.
     122    document = new DocumentImpl(original.getDocument().getDoctype());
     123    root = (Element) document.importNode(original.getDocument().getDocumentElement(), true);
     124    document.appendChild(root);
     125    elements = root.getElementsByTagName("Element");
     126    file = original.getFile();
     127    // Now for each element read in its value tree if present.
     128    for(int i = elements.getLength() - 1; i >= 0; i--) {
     129        ElementWrapper value_element_wrapper = new ElementWrapper((Element)elements.item(i));
     130        GValueModel value_tree = original.getValueTree(value_element_wrapper);
     131        Document value_document = value_tree.getDocument();
     132        Document value_document_copy = new DocumentImpl(value_document.getDoctype());
     133        Element value_element = value_document.getDocumentElement();
     134        Element value_element_copy = (Element) value_document_copy.importNode(value_element, true);
     135        value_document_copy.appendChild(value_element_copy);
     136        GValueModel value_tree_copy = new GValueModel(value_element_wrapper, value_document_copy);
     137        value_trees.put(value_element_wrapper, value_tree_copy);
     138    }
     139    }
     140    /** Conditional copy constructor.
     141     * @param original The original metadata set to copy from.
     142     * @param condition An <i>int</i> which matches one of the tree pruning filter types.
     143     */
     144    public MetadataSet(MetadataSet original, int condition) {
     145    this(original);
     146    // Now based on condition, we may have to remove some nodes from
     147    // this model.
     148    switch(condition) {
     149    case ALL_VALUES:
    150150                // Do nothing.
    151                 break;
    152           case SUBJECTS_ONLY:
     151        break;
     152    case SUBJECTS_ONLY:
    153153                // For each element retrieve its AssignedValues element.
    154                 for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) {
    155                      ElementWrapper value_element = (ElementWrapper)keys.nextElement();
    156                      GValueModel value_tree = (GValueModel)value_trees.get(value_element);
    157                      Document value_tree_document = value_tree.getDocument();
    158                      Element value_tree_root_element = value_tree_document.getDocumentElement();
    159                      // Traverse tree and remove leaf nodes.
    160                      MSMUtils.traverseTree(value_tree_root_element, MSMUtils.NONE, true);
    161                 }
    162                 break;
    163           case NO_VALUES:
     154        for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) {
     155        ElementWrapper value_element = (ElementWrapper)keys.nextElement();
     156        GValueModel value_tree = (GValueModel)value_trees.get(value_element);
     157        Document value_tree_document = value_tree.getDocument();
     158        Element value_tree_root_element = value_tree_document.getDocumentElement();
     159        // Traverse tree and remove leaf nodes.
     160        MSMUtils.traverseTree(value_tree_root_element, MSMUtils.NONE, true);
     161        }
     162        break;
     163    case NO_VALUES:
    164164                // Remove assigned values trees.
    165                 value_trees.clear();
    166                 break;
    167           }
    168     }
    169 
    170     /** Add a mds level attribute.
    171       * @param name The name of the attribute to add as a <Strong>String</strong>.
    172       * @param value The value as a <strong>String</strong>.
    173       */
    174     public void addAttribute(String name, String value) {
    175           root.setAttribute(name, value);
    176     }
    177 
    178     /** Add a new default metadata element with the given name to this metadata set.
    179       * @param name The name of this element as a <strong>String</strong>.
    180       * @return An <strong>ElementWrapper</strong> around the newly created element or null if the element was not created.
    181       */
    182     public ElementWrapper addElement(String name) {
    183           Text text = document.createTextNode(name);
    184           Element identifier = document.createElementNS("","Attribute");
    185           identifier.setAttribute("name","identifier");
    186           identifier.appendChild(text);
    187           Element element = document.createElementNS("","Element");
    188           element.setAttribute("name",name);
    189           element.appendChild(identifier);
    190           root.appendChild(element);
    191           ElementWrapper wrapper = new ElementWrapper(element);
    192           ///ystem.err.println("Added a new element " + wrapper);
    193           return wrapper;
    194     }
    195 
    196     /** Method to add a new metadata element to this metadata set, if and only if the element is not already present.
    197       * @param others_element An <strong>Element</strong> we wish to add to this metadata set, that currently belongs to some other set.
    198       * @return <i>null</i> if the add is successful, otherwise a <strong>String</strong> containing an error message (phrase key).
    199       */
    200     public String addElement(Element others_element) {
    201           if(!containsElement(others_element.getAttribute("name"))) {
     165        value_trees.clear();
     166        break;
     167    }
     168    }
     169
     170    /** Add a mds level attribute.
     171     * @param name The name of the attribute to add as a <Strong>String</strong>.
     172     * @param value The value as a <strong>String</strong>.
     173     */
     174    public void addAttribute(String name, String value) {
     175    root.setAttribute(name, value);
     176    }
     177
     178    /** Add a new default metadata element with the given name to this metadata set.
     179     * @param name The name of this element as a <strong>String</strong>.
     180     * @return An <strong>ElementWrapper</strong> around the newly created element or null if the element was not created.
     181     */
     182    public ElementWrapper addElement(String name) {
     183    Text text = document.createTextNode(name);
     184    Element identifier = document.createElementNS("","Attribute");
     185    identifier.setAttribute("name","identifier");
     186    identifier.appendChild(text);
     187    Element element = document.createElementNS("","Element");
     188    element.setAttribute("name",name);
     189    element.appendChild(identifier);
     190    root.appendChild(element);
     191    ElementWrapper wrapper = new ElementWrapper(element);
     192    ///ystem.err.println("Added a new element " + wrapper);
     193    return wrapper;
     194    }
     195
     196    /** Method to add a new metadata element to this metadata set, if and only if the element is not already present.
     197     * @param others_element An <strong>Element</strong> we wish to add to this metadata set, that currently belongs to some other set.
     198     * @return <i>null</i> if the add is successful, otherwise a <strong>String</strong> containing an error message (phrase key).
     199     */
     200    public String addElement(Element others_element) {
     201    if(!containsElement(others_element.getAttribute("name"))) {
    202202                // First get ownership of the new element, then add it.
    203                 Node our_element = document.importNode(others_element, true);
    204                 root.appendChild(our_element);
    205                 return null;
    206           }
    207           else {
    208                 return "Element name already exists!";
    209           }
    210     }
    211 
    212     /** Method to add a new metadata element with the specified new name to this metadata set, if and only if the name is not already in use.
    213       * @param others_element An <strong>Element</strong> we wish to add to this metadata set, that currently belongs to some other set.
    214       * @param new_name The new name to be given this element, as a <strong>String</strong>.
    215       * @return <i>null</i> if the add is successful, otherwise a <strong>String</strong> containing an error message (phrase key).
    216       */
    217     public String addElement(Element others_element, String new_name) {
    218           if(!containsElement(new_name)) {
     203        Node our_element = document.importNode(others_element, true);
     204        root.appendChild(our_element);
     205        return null;
     206    }
     207    else {
     208        return "Element name already exists!";
     209    }
     210    }
     211
     212    /** Method to add a new metadata element with the specified new name to this metadata set, if and only if the name is not already in use.
     213     * @param others_element An <strong>Element</strong> we wish to add to this metadata set, that currently belongs to some other set.
     214     * @param new_name The new name to be given this element, as a <strong>String</strong>.
     215     * @return <i>null</i> if the add is successful, otherwise a <strong>String</strong> containing an error message (phrase key).
     216     */
     217    public String addElement(Element others_element, String new_name) {
     218    if(!containsElement(new_name)) {
    219219                // First get ownership of the new element, then add it.
    220                 Element our_element =
    221                      (Element) document.importNode(others_element, true);
     220        Element our_element =
     221        (Element) document.importNode(others_element, true);
    222222                // Change name
    223                 our_element.setAttribute("name", new_name);
     223        our_element.setAttribute("name", new_name);
    224224                // Add it
    225                 root.appendChild(our_element);
    226                 return null;
    227           }
    228           else {
    229                 return "MSMPrompt.Name_Exists";
    230           }
    231     }
    232     /** Add a value tree to a given metadata element.
    233       * @param element The <strong>ElementWrapper</strong> containing the element you wish to add a value tree for.
    234       * @param value_tree The root <strong>Element</strong> of the value tree.
    235       */
    236     public void addValueTree(ElementWrapper element, GValueModel model) {
    237           ///ystem.err.println("Adding value tree for " + element.toString());
    238           value_trees.put(element, model);
    239     }
    240 
    241     public int compare(Element e1, Element e2) {
    242           int result = 0;
    243           // Check that they're not the same element.
    244           if(e1 != e2) {
    245                 int index_e1 = -1;
    246                 int index_e2 = -1;
     225        root.appendChild(our_element);
     226        return null;
     227    }
     228    else {
     229        return "MSMPrompt.Name_Exists";
     230    }
     231    }
     232    /** Add a value tree to a given metadata element.
     233     * @param element The <strong>ElementWrapper</strong> containing the element you wish to add a value tree for.
     234     * @param value_tree The root <strong>Element</strong> of the value tree.
     235     */
     236    public void addValueTree(ElementWrapper element, GValueModel model) {
     237    ///ystem.err.println("Adding value tree for " + element.toString());
     238    value_trees.put(element, model);
     239    }
     240
     241    public int compare(Element e1, Element e2) {
     242    int result = 0;
     243    // Check that they're not the same element.
     244    if(e1 != e2) {
     245        int index_e1 = -1;
     246        int index_e2 = -1;
    247247                // Locate the indexes for each element.
    248                 for(int i = 0; i < elements.getLength(); i++) {
    249                      Node element = elements.item(i);
    250                      if(element == e1) {
    251                           index_e1 = i;
    252                      }
    253                      if(elements == e2) {
    254                           index_e2 = i;
    255                      }
    256                 }
    257                 if(index_e1 < index_e2) {
    258                      result = -1;
    259                 }
    260                 else {
    261                      result = 1;
    262                 }
    263           }
    264           return result;
    265     }
    266 
    267     /** A method to determine if this metadata set contains an element with a certain name.
    268       * @param name A <strong>String</strong> which is the name of the element whose presence we are checking.
    269       * @return A <i>boolean</i> which is <i>true</i> if the named element exists, <i>false</i> otherwise.
    270       */
    271     public boolean containsElement(String name) {
    272           for(int i = 0; i < elements.getLength(); i++) {
    273                 Element sibling = (Element) elements.item(i);
    274                 String sibling_name = sibling.getAttribute("name");
    275                 if(sibling_name.equals(name)) {
    276                      return true;
    277                 }
    278           }
    279           return false;
    280     }
    281 
    282     public NamedNodeMap getAttributes() {
    283           return root.getAttributes();
    284     }
    285 
    286     /** Method to retrieve the contact address of the metadata set creator.
    287       * @return A <strong>String</strong> containing the address.
    288       */
    289     public String getContact() {
    290           return root.getAttribute("contact");
    291     }
    292     /** Method to retrieve the name of the creator of this metadata set.
    293       * @return A <strong>String</strong> containing the name.
    294       */
    295     public String getCreator() {
    296           return root.getAttribute("creator");
    297     }
    298     /** Method to retrieve the description of this metadata set. Note that this is language specific, so we determine the desired language from the Dictionary. If no such entry exists, first try returning the english version and failing that the first description found.
    299       * @return The description as a <strong>String</strong>.
    300       */
    301     public String getDescription() {
    302           if(description == null) {
     248        for(int i = 0; i < elements.getLength(); i++) {
     249        Node element = elements.item(i);
     250        if(element == e1) {
     251            index_e1 = i;
     252        }
     253        if(elements == e2) {
     254            index_e2 = i;
     255        }
     256        }
     257        if(index_e1 < index_e2) {
     258        result = -1;
     259        }
     260        else {
     261        result = 1;
     262        }
     263    }
     264    return result;
     265    }
     266
     267    /** A method to determine if this metadata set contains an element with a certain name.
     268     * @param name A <strong>String</strong> which is the name of the element whose presence we are checking.
     269     * @return A <i>boolean</i> which is <i>true</i> if the named element exists, <i>false</i> otherwise.
     270     */
     271    public boolean containsElement(String name) {
     272    for(int i = 0; i < elements.getLength(); i++) {
     273        Element sibling = (Element) elements.item(i);
     274        String sibling_name = sibling.getAttribute("name");
     275        if(sibling_name.equals(name)) {
     276        return true;
     277        }
     278    }
     279    return false;
     280    }
     281
     282    public NamedNodeMap getAttributes() {
     283    return root.getAttributes();
     284    }
     285
     286    /** Method to retrieve the contact address of the metadata set creator.
     287     * @return A <strong>String</strong> containing the address.
     288     */
     289    public String getContact() {
     290    return root.getAttribute("contact");
     291    }
     292    /** Method to retrieve the name of the creator of this metadata set.
     293     * @return A <strong>String</strong> containing the name.
     294     */
     295    public String getCreator() {
     296    return root.getAttribute("creator");
     297    }
     298    /** Method to retrieve the description of this metadata set. Note that this is language specific, so we determine the desired language from the Dictionary. If no such entry exists, first try returning the english version and failing that the first description found.
     299     * @return The description as a <strong>String</strong>.
     300     */
     301    public String getDescription() {
     302    if(description == null) {
    303303                // Determine the code.
    304                 String language_code = Gatherer.dictionary.getLanguage();
     304        String language_code = Gatherer.dictionary.getLanguage();
    305305                // Recover all Description elements
    306                 NodeList descriptions = document.getElementsByTagName("Description");
     306        NodeList descriptions = document.getElementsByTagName("Description");
    307307                // Iterate through the available descriptions looking for the appropriate one. Also make note of the first description, then overwrite it with any english one.
    308                 boolean found = false;
    309                 for(int i = 0; !found && i < descriptions.getLength(); i++) {
    310                      Element pos_description = (Element) descriptions.item(i);
    311                      String pos_description_code = pos_description.getAttribute("language");
    312                      if(pos_description_code.equalsIgnoreCase(language_code)) {
    313                           description = MSMUtils.getValue(pos_description);
    314                           found = true;
    315                      }
    316                      else if(pos_description_code.equalsIgnoreCase("en")) {
    317                           description = MSMUtils.getValue(pos_description);
    318                      }
    319                      else if(description == null) {
    320                           description = MSMUtils.getValue(pos_description);
    321                      }
    322                      pos_description_code = null;
    323                      pos_description = null;
    324                 }
    325                 descriptions = null;
    326                 language_code = null;
     308        boolean found = false;
     309        for(int i = 0; !found && i < descriptions.getLength(); i++) {
     310        Element pos_description = (Element) descriptions.item(i);
     311        String pos_description_code = pos_description.getAttribute("language");
     312        if(pos_description_code.equalsIgnoreCase(language_code)) {
     313            description = MSMUtils.getValue(pos_description);
     314            found = true;
     315        }
     316        else if(pos_description_code.equalsIgnoreCase("en")) {
     317            description = MSMUtils.getValue(pos_description);
     318        }
     319        else if(description == null) {
     320            description = MSMUtils.getValue(pos_description);
     321        }
     322        pos_description_code = null;
     323        pos_description = null;
     324        }
     325        descriptions = null;
     326        language_code = null;
    327327                // Failing all that set an error message
    328                 if(description == null) {
    329                      description = Gatherer.dictionary.get("MSM.No_Description");
    330                 }
    331           }
    332           return description;
    333     }
    334     /** Method to retrieve the <strong>Document</strong> associated with this metadata set.
    335       * @return The <strong>Document</strong> representing this metadata set.
    336       */
    337     public Document getDocument() {
    338           return document;
    339     }
    340     /** Method to retrieve the metadata element indicated by an index.
    341       * @param index An <i>int</i> specifying the required element.
    342       * @return The <strong>Element</strong> at the index.
    343       */
    344     public Element getElement(int index) {
    345           return (Element)elements.item(index);
    346     }
    347     /** This method is used to acquire a reference to the element which matches the given metadata. Note that this is not the same as <i>metadata.getElement()</i> as the reference returned by it may now be obsolete.
    348       * @param metadata A <strong>Metadata</strong> object representing an element and value assignment.
    349       * @return A 'live' reference to an <strong>Element</strong> which is the same as that referenced by the given metadata, or <i>null</i> if there is no such element.
    350       */
    351     public Element getElement(Metadata metadata) {
    352           return metadata.getElement().getElement();
    353     }
    354     /** This method is used to acquire a reference to the element which has the name specified. Note that this is not the same as <i>metadata.getElement()</i> as the reference returned by it may now be obsolete.
    355       * @param name A <strong>String</strong> stating the desired objects name.
    356       * @return A 'live' reference to an <strong>Element</strong> which is the same as that referenced by the given metadata, or <i>null</i> if there is no such element.
    357       */
    358     public Element getElement(String name) {
    359           // Strip any namespace.
    360           while(name.indexOf(".") != -1 && !name.equals(".")) {
    361                 name = name.substring(name.indexOf(".") + 1);
    362           }
    363           ///ystem.err.println("Get element named " + name);
    364           for(int i = 0; i < elements.getLength(); i++) {
    365                 Element element = (Element) elements.item(i);
     328        if(description == null) {
     329        description = Gatherer.dictionary.get("MSM.No_Description");
     330        }
     331    }
     332    return description;
     333    }
     334    /** Method to retrieve the <strong>Document</strong> associated with this metadata set.
     335     * @return The <strong>Document</strong> representing this metadata set.
     336     */
     337    public Document getDocument() {
     338    return document;
     339    }
     340    /** Method to retrieve the metadata element indicated by an index.
     341     * @param index An <i>int</i> specifying the required element.
     342     * @return The <strong>Element</strong> at the index.
     343     */
     344    public Element getElement(int index) {
     345    return (Element)elements.item(index);
     346    }
     347    /** This method is used to acquire a reference to the element which matches the given metadata. Note that this is not the same as <i>metadata.getElement()</i> as the reference returned by it may now be obsolete.
     348     * @param metadata A <strong>Metadata</strong> object representing an element and value assignment.
     349     * @return A 'live' reference to an <strong>Element</strong> which is the same as that referenced by the given metadata, or <i>null</i> if there is no such element.
     350     */
     351    public Element getElement(Metadata metadata) {
     352    return metadata.getElement().getElement();
     353    }
     354    /** This method is used to acquire a reference to the element which has the name specified. Note that this is not the same as <i>metadata.getElement()</i> as the reference returned by it may now be obsolete.
     355     * @param name A <strong>String</strong> stating the desired objects name.
     356     * @return A 'live' reference to an <strong>Element</strong> which is the same as that referenced by the given metadata, or <i>null</i> if there is no such element.
     357     */
     358    public Element getElement(String name) {
     359    // Strip any namespace.
     360    while(name.indexOf(".") != -1 && !name.equals(".")) {
     361        name = name.substring(name.indexOf(".") + 1);
     362    }
     363    ///ystem.err.println("Get element named " + name);
     364    for(int i = 0; i < elements.getLength(); i++) {
     365        Element element = (Element) elements.item(i);
    366366                ///ystem.err.println("Compare to: " + element.getAttribute("name"));
    367                 if(element.getAttribute("name").equals(name)) {
    368                      return element;
    369                 }
    370           }
    371           return null;
    372     }
    373     /** Method to acquire a list of all the elements in this metadata set.
    374       * @return A <strong>NodeList</strong> containing all of this sets elements.
    375       */
    376     public NodeList getElements() {
    377           return elements;
    378     }
    379     /** Method to retrieve the original file this metadata set was created from.
    380       * @return A <strong>File</strong>.
    381       */
    382     public File getFile() {
    383           return file;
    384     }
    385     /** Get the last changed attribute.
    386       * @return Last changed as a <strong>String</strong>.
    387       */
    388     public String getLastChanged() {
    389           return root.getAttribute("lastchanged");
    390     }
    391     /** Method to get this metadata sets name. Note that this is language specific, so we determine the desired language from the Dictionary. If no such entry exists, first try returning the english version and failing that the first name found.
    392       * @return A <strong>String</strong> which contains its name.
    393       */
    394     public String getName() {
    395           if(name == null) {
     367        if(element.getAttribute("name").equals(name)) {
     368        return element;
     369        }
     370    }
     371    return null;
     372    }
     373    /** Method to acquire a list of all the elements in this metadata set.
     374     * @return A <strong>NodeList</strong> containing all of this sets elements.
     375     */
     376    public NodeList getElements() {
     377    return elements;
     378    }
     379    /** Method to retrieve the original file this metadata set was created from.
     380     * @return A <strong>File</strong>.
     381     */
     382    public File getFile() {
     383    return file;
     384    }
     385    /** Get the last changed attribute.
     386     * @return Last changed as a <strong>String</strong>.
     387     */
     388    public String getLastChanged() {
     389    return root.getAttribute("lastchanged");
     390    }
     391    /** Method to get this metadata sets name. Note that this is language specific, so we determine the desired language from the Dictionary. If no such entry exists, first try returning the english version and failing that the first name found.
     392     * @return A <strong>String</strong> which contains its name.
     393     */
     394    public String getName() {
     395    if(name == null) {
    396396                // Determine the code.
    397                 String language_code = Gatherer.dictionary.getLanguage();
     397        String language_code = Gatherer.dictionary.getLanguage();
    398398                // Recover all Name elements
    399                 NodeList names = document.getElementsByTagName("Name");
     399        NodeList names = document.getElementsByTagName("Name");
    400400                // Iterate through the available names looking for the appropriate one. Also make note of the first name, then overwrite it with any english one.
    401                 boolean found = false;
    402                 for(int i = 0; !found && i < names.getLength(); i++) {
    403                      Element pos_name = (Element) names.item(i);
    404                      String pos_name_code = pos_name.getAttribute("language");
    405                      if(pos_name_code.equalsIgnoreCase(language_code)) {
    406                           name = MSMUtils.getValue(pos_name);
    407                           found = true;
    408                      }
    409                      else if(pos_name_code.equalsIgnoreCase("en")) {
    410                           name = MSMUtils.getValue(pos_name);
    411                      }
    412                      else if(name == null) {
    413                           name = MSMUtils.getValue(pos_name);
    414                      }
    415                      pos_name_code = null;
    416                      pos_name = null;
    417                 }
    418                 names = null;
    419                 language_code = null;
     401        boolean found = false;
     402        for(int i = 0; !found && i < names.getLength(); i++) {
     403        Element pos_name = (Element) names.item(i);
     404        String pos_name_code = pos_name.getAttribute("language");
     405        if(pos_name_code.equalsIgnoreCase(language_code)) {
     406            name = MSMUtils.getValue(pos_name);
     407            found = true;
     408        }
     409        else if(pos_name_code.equalsIgnoreCase("en")) {
     410            name = MSMUtils.getValue(pos_name);
     411        }
     412        else if(name == null) {
     413            name = MSMUtils.getValue(pos_name);
     414        }
     415        pos_name_code = null;
     416        pos_name = null;
     417        }
     418        names = null;
     419        language_code = null;
    420420                // Failing all that set an error message
    421                 if(name == null) {
    422                      name = Gatherer.dictionary.get("MSM.No_Name");
    423                 }
    424           }
    425           return name;
    426     }
    427     /** Method to retrieve this metadata sets namespace.
    428       * @return The namespace as a <strong>String</strong>.
    429       */
    430     public String getNamespace() {
    431           return root.getAttribute("namespace");
    432     }
    433     /** Method to retrieve the root element, i.e. the Document Element, of the DOM model behind this metadata set.
    434       * @return An <strong>Element</strong> which is at the root of the modal.
    435       */
    436     public Element getRoot() {
    437           return root;
    438     }
    439     /** Retrieve the value tree from this set that matches the given element.
    440       * @param element The target <strong>ElementWrapper</strong>.
    441       * @return A <strong>GValueModel</strong> value tree, or <i>null</i> if no such element or value tree.
    442       */
    443     public GValueModel getValueTree(ElementWrapper element) {
    444           GValueModel value_tree = null;
    445           ///ystem.err.println("Retrieving value tree for element: " + element.toString());
    446           // Stinking hashtable get doesn't use the overridden equals. So I'll do a loop, which should be pretty small ie O(n) for n metadata elements.
    447           for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) {
    448                 ElementWrapper sibling = (ElementWrapper) keys.nextElement();
    449                 if(sibling.equals(element)) {
    450                      value_tree = (GValueModel) value_trees.get(sibling);
    451                 }
    452           }
    453           // If we've found no value tree, create a new one.
    454           if(value_tree == null) {
    455                 value_tree = new GValueModel(element);
    456                 value_trees.put(element, value_tree);
    457           }
    458           return value_tree;
    459     }
    460     /** Remove a mds level attribute.
    461       * @param name The name of the attribute to remove.
    462       */
    463     public void removeAttribute(String name) {
    464           root.removeAttribute(name);
    465     }
    466     /** Method to remove the given element from this metadata set.
    467       * @param element The <strong>Element</strong> to be removed.
    468       */
    469     public void removeElement(Element element) {
    470           root.removeChild(element);
    471     }
    472     /** Used to remove the value tree for a specific element.
    473       * @param element The <strong>ElementWrapper</strong> whose tree you wish to remove.
    474       * @return The <strong>Element</strong> at the root of the value tree we just removed.
    475       */
    476     public Element removeValueTree(ElementWrapper element) {
    477           for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) {
    478                 ElementWrapper sibling = (ElementWrapper) keys.nextElement();
    479                 if(sibling.equals(element)) {
    480                      Element tree_root = (Element) value_trees.get(sibling);
    481                      value_trees.remove(sibling);
    482                      return tree_root;
    483                 }
    484           }
    485           return null;
    486     }
    487     /** Set one of the mds level attributes.
    488       * @param name The attribute to change.
    489       * @param value its new value.
    490       */
    491     public void setAttribute(String name, String value) {
    492           root.setAttribute(name, value);
    493     }
    494     /** Once the metadata set has been saved to a different location, this is used to update the file parameter.
    495       * @param file The new location of this metadata set <strong>File</strong>.
    496       */
    497     public void setFile(File file) {
    498           this.file = file;
    499     }
    500 
    501     public void setName(String name) {
    502           // Retrieve the name element. We look for the first english one.
    503           Element name_element = null;
    504           Element metadataset_element = document.getDocumentElement();
    505           NodeList name_elements = metadataset_element.getElementsByTagName(Utility.NAME_ELEMENT);
    506           for(int i = 0; i < name_elements.getLength(); i++) {
    507                 Element possible_name_element = (Element) name_elements.item(i);
    508                 if(possible_name_element.getAttribute(Utility.LANGUAGE_ATTRIBUTE).equals(Utility.ENGLISH_VALUE)) {
    509                      // Found it.
    510                      name_element = possible_name_element;
    511                 }
    512           }
    513           // If there is none add one. Note that we can only add english metadata sets. Although others can edit them to add further names as necessary.
    514           if(name_element == null) {
    515                 name_element = document.createElement(Utility.NAME_ELEMENT);
    516                 name_element.setAttribute(Utility.LANGUAGE_ATTRIBUTE, Utility.ENGLISH_VALUE);
    517                 metadataset_element.insertBefore(name_element, metadataset_element.getFirstChild());
    518           }
    519           // Replace the text node
    520           while(name_element.hasChildNodes()) {
    521                 name_element.removeChild(name_element.getFirstChild());
    522           }
    523           name_element.appendChild(document.createTextNode(name));
    524     }
    525 
    526     /** Method to determine the number of elements in this set.
    527       * @return An <i>int</i> specifying the element count.
    528       */
    529     public int size() {
    530           return elements.getLength();
    531     }
    532     /** Method to translate this class into a meaningful string, which in this case is the metadata sets name.
    533       * @return The metadata sets name as a <strong>String</strong>.
    534       */
    535     public String toString() {
    536           String name = getName();
    537           // If there is no given name, then use the namespace as there is garaunteed to be one of them.
    538           if(name == null || name.length() == 0) {
    539                 name = root.getAttribute("namespace");
    540           }
    541           // Append namespace
    542           String namespace = root.getAttribute("namespace");
    543           if(namespace == null || namespace.equals("")) {
    544                 namespace = Utility.EXTRACTED_METADATA_NAMESPACE;
    545           }
    546           name = name + " (" + namespace + ")";
    547           return name;
    548     }
    549 
    550     private void init(File file) {
    551           this.file = file;
    552           this.value_trees = new Hashtable();
    553           this.document = Utility.parse(file, false);
    554           if(document != null) {
    555                 this.elements = document.getElementsByTagName("Element");
    556                 this.root = document.getDocumentElement();
     421        if(name == null) {
     422        name = Gatherer.dictionary.get("MSM.No_Name");
     423        }
     424    }
     425    return name;
     426    }
     427    /** Method to retrieve this metadata sets namespace.
     428     * @return The namespace as a <strong>String</strong>.
     429     */
     430    public String getNamespace() {
     431    return root.getAttribute("namespace");
     432    }
     433    /** Method to retrieve the root element, i.e. the Document Element, of the DOM model behind this metadata set.
     434     * @return An <strong>Element</strong> which is at the root of the modal.
     435     */
     436    public Element getRoot() {
     437    return root;
     438    }
     439    /** Retrieve the value tree from this set that matches the given element.
     440     * @param element The target <strong>ElementWrapper</strong>.
     441     * @return A <strong>GValueModel</strong> value tree, or <i>null</i> if no such element or value tree.
     442     */
     443    public GValueModel getValueTree(ElementWrapper element) {
     444    GValueModel value_tree = null;
     445    ///ystem.err.println("Retrieving value tree for element: " + element.toString());
     446    // Stinking hashtable get doesn't use the overridden equals. So I'll do a loop, which should be pretty small ie O(n) for n metadata elements.
     447    for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) {
     448        ElementWrapper sibling = (ElementWrapper) keys.nextElement();
     449        if(sibling.equals(element)) {
     450        value_tree = (GValueModel) value_trees.get(sibling);
     451        }
     452    }
     453    // If we've found no value tree, create a new one.
     454    if(value_tree == null) {
     455        value_tree = new GValueModel(element);
     456        value_trees.put(element, value_tree);
     457    }
     458    return value_tree;
     459    }
     460    /** Remove a mds level attribute.
     461     * @param name The name of the attribute to remove.
     462     */
     463    public void removeAttribute(String name) {
     464    root.removeAttribute(name);
     465    }
     466    /** Method to remove the given element from this metadata set.
     467     * @param element The <strong>Element</strong> to be removed.
     468     */
     469    public void removeElement(Element element) {
     470    root.removeChild(element);
     471    }
     472    /** Used to remove the value tree for a specific element.
     473     * @param element The <strong>ElementWrapper</strong> whose tree you wish to remove.
     474     * @return The <strong>Element</strong> at the root of the value tree we just removed.
     475     */
     476    public Element removeValueTree(ElementWrapper element) {
     477    for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) {
     478        ElementWrapper sibling = (ElementWrapper) keys.nextElement();
     479        if(sibling.equals(element)) {
     480        Element tree_root = (Element) value_trees.get(sibling);
     481        value_trees.remove(sibling);
     482        return tree_root;
     483        }
     484    }
     485    return null;
     486    }
     487    /** Set one of the mds level attributes.
     488     * @param name The attribute to change.
     489     * @param value its new value.
     490     */
     491    public void setAttribute(String name, String value) {
     492    root.setAttribute(name, value);
     493    }
     494    /** Once the metadata set has been saved to a different location, this is used to update the file parameter.
     495     * @param file The new location of this metadata set <strong>File</strong>.
     496     */
     497    public void setFile(File file) {
     498    this.file = file;
     499    }
     500
     501    public void setName(String name) {
     502    // Retrieve the name element. We look for the first english one.
     503    Element name_element = null;
     504    Element metadataset_element = document.getDocumentElement();
     505    NodeList name_elements = metadataset_element.getElementsByTagName(Utility.NAME_ELEMENT);
     506    for(int i = 0; i < name_elements.getLength(); i++) {
     507        Element possible_name_element = (Element) name_elements.item(i);
     508        if(possible_name_element.getAttribute(Utility.LANGUAGE_ATTRIBUTE).equals(Utility.ENGLISH_VALUE)) {
     509        // Found it.
     510        name_element = possible_name_element;
     511        }
     512    }
     513    // If there is none add one. Note that we can only add english metadata sets. Although others can edit them to add further names as necessary.
     514    if(name_element == null) {
     515        name_element = document.createElement(Utility.NAME_ELEMENT);
     516        name_element.setAttribute(Utility.LANGUAGE_ATTRIBUTE, Utility.ENGLISH_VALUE);
     517        metadataset_element.insertBefore(name_element, metadataset_element.getFirstChild());
     518    }
     519    // Replace the text node
     520    while(name_element.hasChildNodes()) {
     521        name_element.removeChild(name_element.getFirstChild());
     522    }
     523    name_element.appendChild(document.createTextNode(name));
     524    }
     525
     526    /** Method to determine the number of elements in this set.
     527     * @return An <i>int</i> specifying the element count.
     528     */
     529    public int size() {
     530    return elements.getLength();
     531    }
     532    /** Method to translate this class into a meaningful string, which in this case is the metadata sets name.
     533     * @return The metadata sets name as a <strong>String</strong>.
     534     */
     535    public String toString() {
     536    String name = getName();
     537    // If there is no given name, then use the namespace as there is garaunteed to be one of them.
     538    if(name == null || name.length() == 0) {
     539        name = root.getAttribute("namespace");
     540    }
     541    // Append namespace
     542    String namespace = root.getAttribute("namespace");
     543    if(namespace == null || namespace.equals("")) {
     544        namespace = Utility.EXTRACTED_METADATA_NAMESPACE;
     545    }
     546    name = name + " (" + namespace + ")";
     547    return name;
     548    }
     549
     550    private void init(File file) {
     551    this.file = file;
     552    this.value_trees = new Hashtable();
     553    this.document = Utility.parse(file, false);
     554    if(document != null) {
     555        this.elements = document.getElementsByTagName("Element");
     556        this.root = document.getDocumentElement();
    557557                // Now for each element read in its value tree if present.
    558                 for(int i = elements.getLength() - 1; i >= 0; i--) {
    559                      ElementWrapper value_element = new ElementWrapper((Element)elements.item(i));
    560                      File value_file = new File(file.getParentFile(), value_element.toString() + ".mdv");
    561                      ///ystem.err.println("Searching for " + value_file.getAbsolutePath());
    562                      if(value_file.exists()) {
    563                           Document value_document = Utility.parse(value_file, false);
    564                           if(value_document != null) {
    565                                 value_trees.put(value_element, new GValueModel(value_element, value_document));
    566                           }
    567                           else {
    568                                 Gatherer.println("Error! Missing mdv file: " + value_file.getAbsolutePath());
    569                           }
    570                      }
    571                 }
    572           }
    573           else {
    574                 Gatherer.println("Error! Missing mds file: " + file.getAbsolutePath());
    575           }
    576     }
     558        for(int i = elements.getLength() - 1; i >= 0; i--) {
     559        ElementWrapper value_element = new ElementWrapper((Element)elements.item(i));
     560        File value_file = new File(file.getParentFile(), value_element.toString() + ".mdv");
     561        ///ystem.err.println("Searching for " + value_file.getAbsolutePath());
     562        if(value_file.exists()) {
     563            Document value_document = Utility.parse(value_file, false);
     564            if(value_document != null) {
     565            value_trees.put(value_element, new GValueModel(value_element, value_document));
     566            }
     567            else {
     568            Gatherer.println("Error! Missing mdv file: " + value_file.getAbsolutePath());
     569            }
     570        }
     571        }
     572    }
     573    else {
     574        Gatherer.println("Error! Missing mds file: " + file.getAbsolutePath());
     575    }
     576    }
    577577}
  • trunk/gli/src/org/greenstone/gatherer/msm/MetadataSetManager.java

    r4362 r4365  
    6666public class MetadataSetManager {
    6767
    68     /** The name of the hidden, or system, metadata set. */
    69     static final public String HIDDEN = "hidden";
    70 
    71     /** A mapping from metadata namespace to metadata set. */
    72     static private Hashtable mds_hashtable = new Hashtable();
    73 
    74     /** The class responsible for creating and maintaining all the visual components of the metadata management package. */
    75     public MSMPrompt prompt = null;
    76     /** The profiler is responsible for remembering what actions a user has requested when importing metadata, so as to prevent the user needlessly re-entering this information for each import. */
    77     public MSMProfiler profiler = null;
    78 
    79     /** Records all of the changes made to metadata as part of the current metadata change. Entries map from a particular FileNode to an ArrayList of the modified metadata for that node. Not to be confused with the undo managers idea of undo which records a list of all metadata changes requested. */
    80     private HashMap undo_buffer = new HashMap();
    81     /** The loader responsible for sequentially loading and attempting to use all registered metadata parsers to extract existing metadata from new files. */
    82     private ExistingMetadataLoader loader = null;
    83     /** Specialized parser for parsing GreenstoneDirectoryMetadata files, which not only caches entries, but also breaks up massive metadata.xml files into reasonable sizes. */
    84     private GDMParser gdm_parser = null;
    85     /** A list of classes who are interested in changes to the loaded metadata sets. */
    86     private Vector listeners = null;
    87 
    88     /** Constructor. */
    89     public MetadataSetManager() {
    90           this.gdm_parser = new GDMParser();
    91           this.listeners = new Vector();
    92           this.loader = new ExistingMetadataLoader();
    93           loadProfiler();
    94           this.prompt = new MSMPrompt(this);
    95     }
    96 
    97     /** Attach a piece of metadata to a record or records, ensuring the value tree is built properly, and correct messaging fired.
    98       * @param records A FileNode[] of records, or directories, to add the specified metadata to.
    99       * @param element The metadata element, contained within an ElementWrapper to base metadata on.
    100       * @param value The value to assign to the metadata as a String.
    101       */
    102     public void addMetadata(long id, FileNode records[], ElementWrapper element, String value_str) {
    103           addMetadata(id, records, element, value_str, MetaEditPrompt.CONFIRM);
    104     }
    105     public void addMetadata(long id, FileNode records[], ElementWrapper element, String value_str, int action) {
    106           // Retrieve the appropriate value node from the value tree for this element, creating it if necessary.
    107           GValueModel model = getValueTree(element);
    108           GValueNode value = null;
    109           if(model != null) {
    110                 value = model.addValue(value_str); // Only adds if not already present, otherwise just returns existing node.
    111           }
    112           else {
    113                 value = new GValueNode(element.toString(), value_str);
    114           }
    115           // Create new metadata.
    116           Metadata metadata = new Metadata(value);
    117           // Reset the undo buffer.
    118           undo_buffer.clear();
    119           // Assign to records. Note that we must watch for responses from the user prompts, and cease loop if break signalled.
    120           // Now add the metadata to each selected file node.
    121           for(int i = 0; action != MetaEditPrompt.CANCEL && i < records.length; i++) {
    122                 action = addMetadata(id, records[i], metadata, action, (records.length > 1));
    123           }
    124           // If we were cancelled we should undo any changes so far
    125           if(action == MetaEditPrompt.CANCEL) {
    126                 for(Iterator keys = undo_buffer.keySet().iterator(); keys.hasNext(); ) {
    127                      FileNode record = (FileNode) keys.next();
    128                      undoAdd(id, record);
    129                 }
    130           }
    131     }
    132 
    133     /** Adds a metadata set listener to this set, if it isn't alreay listening.
    134       * @param listener The new MSMListener.
    135       */
    136     public void addMSMListener(MSMListener listener) {
    137           if(!listeners.contains(listener)) {
    138                 listeners.add(listener);
    139           }
    140     }
    141 
    142     public MetadataSet addSet(String namespace, String name) {
    143           MetadataSet mds = new MetadataSet(Utility.METADATA_SET_TEMPLATE);
    144           mds.setAttribute("creator","The Gatherer");
    145           // Calculate lastchanged to right now on this machine by this user
    146           String user_name = System.getProperty("user.name");
    147           String machine_name = Utility.getMachineName();
    148           mds.setAttribute("lastchanged", Utility.getDateString() + " - " + user_name + " on " + machine_name);
    149           // And the remaining attributes.
    150           //mds.setAttribute("name", name);
    151           mds.setAttribute("namespace", namespace);
    152           mds_hashtable.put(namespace, mds);
    153           // Add the name element.
    154           mds.setName(name);
    155           fireSetChanged(mds);
    156           return mds;
    157     }
    158 
    159     /** Add a value tree to a given metadata element.
    160       * @param element The ElementWrapper containing the element you wish to add a value tree for.
    161       * @param value_tree The root Element of the value tree.
    162       */
    163     public void addValueTree(GValueModel model) {
    164           ElementWrapper element = model.getElement();
    165           String namespace = element.getNamespace();
    166           MetadataSet mds = (MetadataSet) mds_hashtable.get(namespace);
    167           if(mds != null) {
    168                 mds.addValueTree(element, model);
    169           }
    170     }
    171     /** Destructor.
    172       * @see org.greenstone.gatherer.msm.MSMProfiler
    173       */
    174     public void destroy() {
    175           mds_hashtable.clear();
    176           profiler.destroy();
    177           profiler = null;
    178     }
    179     /** Method called to open a metadata set editing window.
    180       * @return A boolean indicating if the edit was successful.
    181       */
    182     public boolean editMDS() {
    183           /* TODO - implement. Something here like: new MSMEditor() */
    184           MetadataEditorManager mem = new MetadataEditorManager();
    185           mem.dispose();
    186           mem = null;
    187           return true;
    188     }
    189 
    190     /** This method is called to export a metadata set. First a prompt is displayed to gather necessary details such as which metadata set to export, where to export it to and what conditions should be applied when exporting. Once this information is gathered the static method <i>exportMDS()</i> is called with the appropriate output stream.
    191       * @return A boolean which is <i>true</i> if the metadata set has been exported successfully, <i>false</i> otherwise.
    192       */
    193     public boolean exportMDS() {
    194           ExportMDSPrompt emdsp = new ExportMDSPrompt(this, true);
    195           int result = emdsp.display();
    196           MetadataSet set = emdsp.getSelectedSet();
    197           if(result == ExportMDSPrompt.EXPORT && set != null) {
    198                 File file = emdsp.getSelectedFile();
    199                 MetadataSet set_copy = new MetadataSet(set, emdsp.getSelectedCondition());
    200                 try {
    201                      file.getParentFile().mkdirs();
    202                      Utility.export(set_copy.getDocument(), file);
    203                      // Now for each element attempt to save its value tree.
    204                      NodeList elements = set_copy.getElements();
    205                      for(int i = elements.getLength() - 1; i >= 0; i--) {
    206                           ElementWrapper value_element = new ElementWrapper((Element)elements.item(i));
    207                           GValueModel value_tree = set_copy.getValueTree(value_element);
    208                           if(value_tree != null) {
    209                                 File value_file = new File(file.getParentFile(), value_element.toString() + ".mdv");
    210                                 ///ystem.err.println("Saving value file: " + value_file.toString());
    211                                 Utility.export(value_tree.getDocument(), value_file);
    212                           }
    213                      }
    214                      return true;
    215                 }
    216                 catch (Exception error) {
    217                      error.printStackTrace();
    218                 }
    219           }
    220           emdsp.dispose();
    221           emdsp = null;
    222           return false;
    223     }
    224 
    225     /** Fire an element changed message off to all registered listeners.
    226       * @param event An MSMEvent detailing the change.
    227       */
    228     public void fireElementChanged(MSMEvent event) {
    229           // Then send it to all the listeners.
    230           for(int i = 0; i < listeners.size(); i++) {
    231                 ((MSMListener)listeners.get(i)).elementChanged(event);
    232           }
    233     }
    234 
    235     /** Fire a metadata value changed message, whose id is to be generated now, off to all registered listeners. */
    236     public void fireMetadataChanged(FileNode node, Metadata old_data, Metadata new_data) {
    237           fireMetadataChanged(System.currentTimeMillis(), node, old_data, new_data);
    238     }
    239 
    240     /** Fire a metadata value changed message off to all registered listeners. */
    241     public void fireMetadataChanged(long id, FileNode node, Metadata old_data, Metadata new_data) {
    242           if(old_data != null) {
    243                 old_data.getElement().dec();
    244           }
    245           if(new_data != null) {
    246                 new_data.getElement().inc();
    247           }
    248           ///ystem.err.println("Metadata changed: " + record + " > '" + old_data + "' -> '" + new_data + "'");
    249           // Create a new MSMEvent based on the record.
    250           MSMEvent event = new MSMEvent(this, id, node, old_data, new_data);
    251           // Then send it to all the listeners.
    252           for(int i = 0; i < listeners.size(); i++) {
    253                 ((MSMListener)listeners.get(i)).metadataChanged(event);
    254           }
    255     }
    256 
    257     /** Fire a metadata set changed message off to all registered listeners.
    258       * @param set The MetadataSet thats changed.
    259       */
    260     public void fireSetChanged(MetadataSet set) {
    261           // Create a new MSMEvent, with a MSMAction containing only the new set.
    262           MSMEvent event = new MSMEvent(this, 0L, new MSMAction(set.toString(), null, -1, null));
    263           // Then send it to all the listeners.
    264           for(int i = 0; i < listeners.size(); i++) {
    265                 ((MSMListener)listeners.get(i)).setChanged(event);
    266           }
    267     }
    268     /** Called whenever the value tree associated with an element changes significantly.
    269       * @param element The metadata element whose value tree has changed, as an ElementWrapper.
    270       */
    271     public void fireValueChanged(ElementWrapper element, GValueModel old_model, GValueModel new_model) {
    272           // Create a new MSMEvent based on the element wrapper.
    273           MSMEvent event = new MSMEvent(this, 0L, element, old_model, new_model);
    274           // Then send it to all the listeners.
    275           for(int i = 0; i < listeners.size(); i++) {
    276                 ((MSMListener)listeners.get(i)).valueChanged(event);
    277           }       
    278     }
    279     /** Builds a list of elements that have been assigned as metadata in this collection. We go through all of the elements, looking for elements whose occurances are greater than 0. A convenience call to the version with one parameter.
    280       * @return A Vector of assigned elements.
    281       */
    282     public Vector getAssignedElements() {
    283           return getAssignedElements(false);
    284     }
    285     /** Builds a list of elements that have been assigned as metadata in this collection. We go through all of the elements, looking for elements whose occurances are greater than 0.
    286       * @param hierarchy_only <i>true</i> to only return those elements that are both assigned and have hierarchical value trees, <i>false</i> for just assignments.
    287       * @return A Vector of assigned elements.
    288       */
    289     public Vector getAssignedElements(boolean hierarchy_only) {
    290           Vector elements = new Vector();
    291           for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) {
    292                 MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement());
    293                 if(!mds.getNamespace().equals(HIDDEN)) {
    294                      NodeList set_elements = mds.getElements();
    295                      for(int i = 0; i < set_elements.getLength(); i++) {
    296                           ElementWrapper element = new ElementWrapper((Element)set_elements.item(i));
    297                           elements.add(element);
    298                      }
    299                 }
    300           }
    301           Collections.sort(elements);
    302           for(int i = elements.size(); i != 0; i--) {
    303                 ElementWrapper element = (ElementWrapper) elements.get(i - 1);
    304                 if(element.getOccurances() == 0 && element.getNamespace().length() > 0 && !element.toString().equals("Source")) {
    305                      elements.remove(element);
    306                 }
    307                 else if(hierarchy_only) {
    308                      GValueModel model = getValueTree(element);
    309                      if(!model.isHierarchy()) {
    310                           elements.remove(element);
    311                      }
    312                 }
    313           }
    314           return elements;
    315     }
    316     /** Used to get all the (non-hidden) elements in this manager.
    317       * @return A Vector of ElementWrappers.
    318       */
    319     public Vector getElements() {
    320           return getElements(false);
    321     }
    322     /** Used to get all the elements in this manager.
    323       * @param all <i>true</i> if all elements, including hidden, should be returned.
    324       * @return A Vector of ElementWrappers.
    325       */
    326     public Vector getElements(boolean all) {
    327           Vector all_elements = new Vector();
    328           for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) {
    329                 MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement());
    330                 if(all || !mds.getNamespace().equals(HIDDEN)) {
    331                      NodeList set_elements = mds.getElements();
    332                      ///ystem.err.println("The set " + mds + " has " + set_elements.getLength() + " elements.");
    333                      for(int i = 0; i < set_elements.getLength(); i++) {
    334                           ElementWrapper element = new ElementWrapper((Element)set_elements.item(i));
    335                           if(!element.toString().equals("Source")) {
    336                                 all_elements.add(element);
    337                           }
    338                      }
    339                 }
    340           }
    341           Collections.sort(all_elements);
    342           return all_elements;
    343     }
    344     /** Returns all the elements within this set as a combobox model.
    345       * @return A MetadataComboBoxModel containing all the metadata elements from all the sets, with namespacing.
    346       */
    347     public MetadataComboBoxModel getElementModel() {
    348           return new MetadataComboBoxModel(this);
    349     }
    350     /** Retrieve a metadata element by its index.
    351       * @param index The specified index as an int.
    352       * @return An ElementWrapper containing the specied element, or <i>null</i> is no such element exists.
    353       */
    354     public ElementWrapper getElement(int index) {
    355           Vector elements = getElements(false);
    356           ElementWrapper result = null;
    357           if(0 <= index && index < elements.size()) {
    358                 result = (ElementWrapper) elements.get(index);
    359           }
    360           return result;
    361     }
    362     /** Retrieve a metadata element by looking at the current metadata element. Note that this 'index' element may now be disconnected from the DOM model, so we have to reload the target element by the string method.
    363       * @param element The possibly out-of-data MetadataElement.
    364       * @return An ElementWrapper containing the specied element, or <i>null</i> is no such element exists.
    365       */
    366     public ElementWrapper getElement(ElementWrapper element) {
    367           return getElement(element.toString());
    368     }
    369     /** Retrieve a metadata element by its fully qualified name.
    370       * @param name The elements name as a String.
    371       * @return An ElementWrapper containing the specied element, or <i>null</i> is no such element exists.
    372       */
    373     public ElementWrapper getElement(String name) {
    374           ///ystem.err.println("Retrieve element " + name);
    375           if(name == null) {
     68    /** The name of the hidden, or system, metadata set. */
     69    static final public String HIDDEN = "hidden";
     70
     71    /** A mapping from metadata namespace to metadata set. */
     72    static private Hashtable mds_hashtable = new Hashtable();
     73
     74    /** The class responsible for creating and maintaining all the visual components of the metadata management package. */
     75    public MSMPrompt prompt = null;
     76    /** The profiler is responsible for remembering what actions a user has requested when importing metadata, so as to prevent the user needlessly re-entering this information for each import. */
     77    public MSMProfiler profiler = null;
     78
     79    /** Records all of the changes made to metadata as part of the current metadata change. Entries map from a particular FileNode to an ArrayList of the modified metadata for that node. Not to be confused with the undo managers idea of undo which records a list of all metadata changes requested. */
     80    private HashMap undo_buffer = new HashMap();
     81    /** The loader responsible for sequentially loading and attempting to use all registered metadata parsers to extract existing metadata from new files. */
     82    private ExistingMetadataLoader loader = null;
     83    /** Specialized parser for parsing GreenstoneDirectoryMetadata files, which not only caches entries, but also breaks up massive metadata.xml files into reasonable sizes. */
     84    private GDMParser gdm_parser = null;
     85    /** A list of classes who are interested in changes to the loaded metadata sets. */
     86    private Vector listeners = null;
     87
     88    /** Constructor. */
     89    public MetadataSetManager() {
     90    this.gdm_parser = new GDMParser();
     91    this.listeners = new Vector();
     92    this.loader = new ExistingMetadataLoader();
     93    loadProfiler();
     94    this.prompt = new MSMPrompt(this);
     95    }
     96
     97    /** Attach a piece of metadata to a record or records, ensuring the value tree is built properly, and correct messaging fired.
     98     * @param records A FileNode[] of records, or directories, to add the specified metadata to.
     99     * @param element The metadata element, contained within an ElementWrapper to base metadata on.
     100     * @param value The value to assign to the metadata as a String.
     101     */
     102    public void addMetadata(long id, FileNode records[], ElementWrapper element, String value_str) {
     103    addMetadata(id, records, element, value_str, MetaEditPrompt.CONFIRM);
     104    }
     105    public void addMetadata(long id, FileNode records[], ElementWrapper element, String value_str, int action) {
     106    // Retrieve the appropriate value node from the value tree for this element, creating it if necessary.
     107    GValueModel model = getValueTree(element);
     108    GValueNode value = null;
     109    if(model != null) {
     110        value = model.addValue(value_str); // Only adds if not already present, otherwise just returns existing node.
     111    }
     112    else {
     113        value = new GValueNode(element.toString(), value_str);
     114    }
     115    // Create new metadata.
     116    Metadata metadata = new Metadata(value);
     117    // Reset the undo buffer.
     118    undo_buffer.clear();
     119    // Assign to records. Note that we must watch for responses from the user prompts, and cease loop if break signalled.
     120    // Now add the metadata to each selected file node.
     121    for(int i = 0; action != MetaEditPrompt.CANCEL && i < records.length; i++) {
     122        action = addMetadata(id, records[i], metadata, action, (records.length > 1));
     123    }
     124    // If we were cancelled we should undo any changes so far
     125    if(action == MetaEditPrompt.CANCEL) {
     126        for(Iterator keys = undo_buffer.keySet().iterator(); keys.hasNext(); ) {
     127        FileNode record = (FileNode) keys.next();
     128        undoAdd(id, record);
     129        }
     130    }
     131    }
     132
     133    /** Adds a metadata set listener to this set, if it isn't alreay listening.
     134     * @param listener The new MSMListener.
     135     */
     136    public void addMSMListener(MSMListener listener) {
     137    if(!listeners.contains(listener)) {
     138        listeners.add(listener);
     139    }
     140    }
     141
     142    public MetadataSet addSet(String namespace, String name) {
     143    MetadataSet mds = new MetadataSet(Utility.METADATA_SET_TEMPLATE);
     144    mds.setAttribute("creator","The Gatherer");
     145    // Calculate lastchanged to right now on this machine by this user
     146    String user_name = System.getProperty("user.name");
     147    String machine_name = Utility.getMachineName();
     148    mds.setAttribute("lastchanged", Utility.getDateString() + " - " + user_name + " on " + machine_name);
     149    // And the remaining attributes.
     150    //mds.setAttribute("name", name);
     151    mds.setAttribute("namespace", namespace);
     152    mds_hashtable.put(namespace, mds);
     153    // Add the name element.
     154    mds.setName(name);
     155    fireSetChanged(mds);
     156    return mds;
     157    }
     158
     159    /** Add a value tree to a given metadata element.
     160     * @param element The ElementWrapper containing the element you wish to add a value tree for.
     161     * @param value_tree The root Element of the value tree.
     162     */
     163    public void addValueTree(GValueModel model) {
     164    ElementWrapper element = model.getElement();
     165    String namespace = element.getNamespace();
     166    MetadataSet mds = (MetadataSet) mds_hashtable.get(namespace);
     167    if(mds != null) {
     168        mds.addValueTree(element, model);
     169    }
     170    }
     171    /** Destructor.
     172     * @see org.greenstone.gatherer.msm.MSMProfiler
     173     */
     174    public void destroy() {
     175    mds_hashtable.clear();
     176    profiler.destroy();
     177    profiler = null;
     178    }
     179    /** Method called to open a metadata set editing window.
     180     * @return A boolean indicating if the edit was successful.
     181     */
     182    public boolean editMDS() {
     183    /* TODO - implement. Something here like: new MSMEditor() */
     184    MetadataEditorManager mem = new MetadataEditorManager();
     185    mem.dispose();
     186    mem = null;
     187    return true;
     188    }
     189
     190    /** This method is called to export a metadata set. First a prompt is displayed to gather necessary details such as which metadata set to export, where to export it to and what conditions should be applied when exporting. Once this information is gathered the static method <i>exportMDS()</i> is called with the appropriate output stream.
     191     * @return A boolean which is <i>true</i> if the metadata set has been exported successfully, <i>false</i> otherwise.
     192     */
     193    public boolean exportMDS() {
     194    ExportMDSPrompt emdsp = new ExportMDSPrompt(this, true);
     195    int result = emdsp.display();
     196    MetadataSet set = emdsp.getSelectedSet();
     197    if(result == ExportMDSPrompt.EXPORT && set != null) {
     198        File file = emdsp.getSelectedFile();
     199        MetadataSet set_copy = new MetadataSet(set, emdsp.getSelectedCondition());
     200        try {
     201        file.getParentFile().mkdirs();
     202        Utility.export(set_copy.getDocument(), file);
     203        // Now for each element attempt to save its value tree.
     204        NodeList elements = set_copy.getElements();
     205        for(int i = elements.getLength() - 1; i >= 0; i--) {
     206            ElementWrapper value_element = new ElementWrapper((Element)elements.item(i));
     207            GValueModel value_tree = set_copy.getValueTree(value_element);
     208            if(value_tree != null) {
     209            File value_file = new File(file.getParentFile(), value_element.toString() + ".mdv");
     210            ///ystem.err.println("Saving value file: " + value_file.toString());
     211            Utility.export(value_tree.getDocument(), value_file);
     212            }
     213        }
     214        return true;
     215        }
     216        catch (Exception error) {
     217        error.printStackTrace();
     218        }
     219    }
     220    emdsp.dispose();
     221    emdsp = null;
     222    return false;
     223    }
     224
     225    /** Fire an element changed message off to all registered listeners.
     226     * @param event An MSMEvent detailing the change.
     227     */
     228    public void fireElementChanged(MSMEvent event) {
     229    // Then send it to all the listeners.
     230    for(int i = 0; i < listeners.size(); i++) {
     231        ((MSMListener)listeners.get(i)).elementChanged(event);
     232    }
     233    }
     234
     235    /** Fire a metadata value changed message, whose id is to be generated now, off to all registered listeners. */
     236    public void fireMetadataChanged(FileNode node, Metadata old_data, Metadata new_data) {
     237    fireMetadataChanged(System.currentTimeMillis(), node, old_data, new_data);
     238    }
     239
     240    /** Fire a metadata value changed message off to all registered listeners. */
     241    public void fireMetadataChanged(long id, FileNode node, Metadata old_data, Metadata new_data) {
     242    if(old_data != null) {
     243        old_data.getElement().dec();
     244    }
     245    if(new_data != null) {
     246        new_data.getElement().inc();
     247    }
     248    ///ystem.err.println("Metadata changed: " + record + " > '" + old_data + "' -> '" + new_data + "'");
     249    // Create a new MSMEvent based on the record.
     250    MSMEvent event = new MSMEvent(this, id, node, old_data, new_data);
     251    // Then send it to all the listeners.
     252    for(int i = 0; i < listeners.size(); i++) {
     253        ((MSMListener)listeners.get(i)).metadataChanged(event);
     254    }
     255    }
     256
     257    /** Fire a metadata set changed message off to all registered listeners.
     258     * @param set The MetadataSet thats changed.
     259     */
     260    public void fireSetChanged(MetadataSet set) {
     261    // Create a new MSMEvent, with a MSMAction containing only the new set.
     262    MSMEvent event = new MSMEvent(this, 0L, new MSMAction(set.toString(), null, -1, null));
     263    // Then send it to all the listeners.
     264    for(int i = 0; i < listeners.size(); i++) {
     265        ((MSMListener)listeners.get(i)).setChanged(event);
     266    }
     267    }
     268    /** Called whenever the value tree associated with an element changes significantly.
     269     * @param element The metadata element whose value tree has changed, as an ElementWrapper.
     270     */
     271    public void fireValueChanged(ElementWrapper element, GValueModel old_model, GValueModel new_model) {
     272    // Create a new MSMEvent based on the element wrapper.
     273    MSMEvent event = new MSMEvent(this, 0L, element, old_model, new_model);
     274    // Then send it to all the listeners.
     275    for(int i = 0; i < listeners.size(); i++) {
     276        ((MSMListener)listeners.get(i)).valueChanged(event);
     277    }         
     278    }
     279    /** Builds a list of elements that have been assigned as metadata in this collection. We go through all of the elements, looking for elements whose occurances are greater than 0. A convenience call to the version with one parameter.
     280     * @return A Vector of assigned elements.
     281     */
     282    public Vector getAssignedElements() {
     283    return getAssignedElements(false);
     284    }
     285    /** Builds a list of elements that have been assigned as metadata in this collection. We go through all of the elements, looking for elements whose occurances are greater than 0.
     286     * @param hierarchy_only <i>true</i> to only return those elements that are both assigned and have hierarchical value trees, <i>false</i> for just assignments.
     287     * @return A Vector of assigned elements.
     288     */
     289    public Vector getAssignedElements(boolean hierarchy_only) {
     290    Vector elements = new Vector();
     291    for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) {
     292        MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement());
     293        if(!mds.getNamespace().equals(HIDDEN)) {
     294        NodeList set_elements = mds.getElements();
     295        for(int i = 0; i < set_elements.getLength(); i++) {
     296            ElementWrapper element = new ElementWrapper((Element)set_elements.item(i));
     297            elements.add(element);
     298        }
     299        }
     300    }
     301    Collections.sort(elements);
     302    for(int i = elements.size(); i != 0; i--) {
     303        ElementWrapper element = (ElementWrapper) elements.get(i - 1);
     304        if(element.getOccurances() == 0 && element.getNamespace().length() > 0 && !element.toString().equals("Source")) {
     305        elements.remove(element);
     306        }
     307        else if(hierarchy_only) {
     308        GValueModel model = getValueTree(element);
     309        if(!model.isHierarchy()) {
     310            elements.remove(element);
     311        }
     312        }
     313    }
     314    return elements;
     315    }
     316    /** Used to get all the (non-hidden) elements in this manager.
     317     * @return A Vector of ElementWrappers.
     318     */
     319    public Vector getElements() {
     320    return getElements(false);
     321    }
     322    /** Used to get all the elements in this manager.
     323     * @param all <i>true</i> if all elements, including hidden, should be returned.
     324     * @return A Vector of ElementWrappers.
     325     */
     326    public Vector getElements(boolean all) {
     327    Vector all_elements = new Vector();
     328    for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) {
     329        MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement());
     330        if(all || !mds.getNamespace().equals(HIDDEN)) {
     331        NodeList set_elements = mds.getElements();
     332        ///ystem.err.println("The set " + mds + " has " + set_elements.getLength() + " elements.");
     333        for(int i = 0; i < set_elements.getLength(); i++) {
     334            ElementWrapper element = new ElementWrapper((Element)set_elements.item(i));
     335            if(!element.toString().equals("Source")) {
     336            all_elements.add(element);
     337            }
     338        }
     339        }
     340    }
     341    Collections.sort(all_elements);
     342    return all_elements;
     343    }
     344    /** Returns all the elements within this set as a combobox model.
     345     * @return A MetadataComboBoxModel containing all the metadata elements from all the sets, with namespacing.
     346     */
     347    public MetadataComboBoxModel getElementModel() {
     348    return new MetadataComboBoxModel(this);
     349    }
     350    /** Retrieve a metadata element by its index.
     351     * @param index The specified index as an int.
     352     * @return An ElementWrapper containing the specied element, or <i>null</i> is no such element exists.
     353     */
     354    public ElementWrapper getElement(int index) {
     355    Vector elements = getElements(false);
     356    ElementWrapper result = null;
     357    if(0 <= index && index < elements.size()) {
     358        result = (ElementWrapper) elements.get(index);
     359    }
     360    return result;
     361    }
     362    /** Retrieve a metadata element by looking at the current metadata element. Note that this 'index' element may now be disconnected from the DOM model, so we have to reload the target element by the string method.
     363     * @param element The possibly out-of-data MetadataElement.
     364     * @return An ElementWrapper containing the specied element, or <i>null</i> is no such element exists.
     365     */
     366    public ElementWrapper getElement(ElementWrapper element) {
     367    return getElement(element.toString());
     368    }
     369    /** Retrieve a metadata element by its fully qualified name.
     370     * @param name The elements name as a String.
     371     * @return An ElementWrapper containing the specied element, or <i>null</i> is no such element exists.
     372     */
     373    public ElementWrapper getElement(String name) {
     374    ///ystem.err.println("Retrieve element " + name);
     375    if(name == null) {
    376376                ///ystem.err.println("No name!");
    377                 return null;
    378           }
    379           ElementWrapper result = null;
    380           MetadataSet set = null;
    381           String element = null;
    382           // First we seperate off what set it is in, where we have '<set><namespace_separator><element>'.
    383           if(name.indexOf(MSMUtils.NS_SEP) != -1) {
    384                 String namespace = name.substring(0, name.indexOf(MSMUtils.NS_SEP));
     377        return null;
     378    }
     379    ElementWrapper result = null;
     380    MetadataSet set = null;
     381    String element = null;
     382    // First we seperate off what set it is in, where we have '<set><namespace_separator><element>'.
     383    if(name.indexOf(MSMUtils.NS_SEP) != -1) {
     384        String namespace = name.substring(0, name.indexOf(MSMUtils.NS_SEP));
    385385                // Retrieve the correct set if possible.
    386                 set = (MetadataSet)mds_hashtable.get(namespace);
    387                 namespace = null;
     386        set = (MetadataSet)mds_hashtable.get(namespace);
     387        namespace = null;
    388388                // Now retrieve the element name.
    389                 element = name.substring(name.indexOf(MSMUtils.NS_SEP) + 1);
    390           }
    391           else {
     389        element = name.substring(name.indexOf(MSMUtils.NS_SEP) + 1);
     390    }
     391    else {
    392392                // No namespace so assume ns = "".
    393                 set = (MetadataSet)mds_hashtable.get("");
    394                 element = name;
    395           }
    396           if(set != null) {
     393        set = (MetadataSet)mds_hashtable.get("");
     394        element = name;
     395    }
     396    if(set != null) {
    397397                ///ystem.err.print("Trying to match element " + element +"?");
    398                 Element temp = set.getElement(element);
    399                 if(temp != null) {
    400                      ///ystem.err.println("Found!");
    401                      result = new ElementWrapper(temp);
     398        Element temp = set.getElement(element);
     399        if(temp != null) {
     400        ///ystem.err.println("Found!");
     401        result = new ElementWrapper(temp);
     402        }
     403        else {
     404        ///ystem.err.println("Not found.");
     405        }
     406        element = null;
     407        temp = null;
     408    }
     409    else {
     410                ///ystem.err.println("No such set");
     411    }
     412    set = null;
     413    if(result == null) {
     414                ///ystem.err.println("Shouldn't return null unless this is greenstone archive parsing.");
     415    }
     416    return result;
     417    }
     418    /** Retrieve a certain named element from a certain named set.
     419     * @param set The metadata set whose element you want.
     420     * @param name The name of the element.
     421     * @return An ElementWrapper around the requested element, or null if no such set or element.
     422     */
     423    public ElementWrapper getElement(String set, String name) {
     424    if(mds_hashtable.containsKey(set)) {
     425        MetadataSet temp = (MetadataSet)mds_hashtable.get(set);
     426        return new ElementWrapper(temp.getElement(name));
     427    }
     428    return null;
     429    }
     430    /** Get all of the metadata elements as an array of nodelists.
     431     * @return A NodeList[] of metadata elements.
     432     */
     433    public NodeList[] getNodeLists() {
     434    NodeList elements[] = null;
     435    int index = 0;
     436    elements = new NodeList[getSets().size()]; // Remember not to count hidden metadata
     437    for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) {
     438        MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement());
     439        if(!mds.getNamespace().equals(HIDDEN)) {
     440        elements[index] = mds.getElements();
     441        index++;
     442        }
     443    }
     444    return elements;
     445    }
     446
     447    /** Retrieve the named metadata set.
     448     * @param name The sets name as a String.
     449     * @return The MetadataSet as named, or null if no such set.
     450     */
     451    public MetadataSet getSet(String name) {
     452    if(mds_hashtable.containsKey(name)) {
     453        return (MetadataSet) mds_hashtable.get(name);
     454    }
     455    else {
     456                ///ystem.err.println("Couldn't find metadata set.");
     457        if(name.equals(HIDDEN)) {
     458        return createHidden();
     459        }
     460    }
     461    return null;
     462    }
     463
     464    /** Method to retrieve all of the metadata sets loaded in this collection.
     465     * @return A Vector of metadata sets.
     466     */
     467    public Vector getSets() {
     468    return getSets(true);
     469    }
     470    public Vector getSets(boolean include_greenstone) {
     471    Vector result = new Vector();
     472    for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) {
     473        MetadataSet set = (MetadataSet)mds_hashtable.get(keys.nextElement());
     474        if(!set.getNamespace().equals(HIDDEN) && (include_greenstone || !set.getNamespace().equals(""))) {
     475        result.add(set);
     476        }
     477    }
     478    return result;
     479    }
     480    /** Find the total number of elements loaded.
     481     * @return The element count as an int.
     482     */
     483    public int getSize() {
     484    int count = 0;
     485    if(mds_hashtable.size() > 0) {
     486        for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements();) {
     487        MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement());
     488        if(mds.getNamespace().equals(HIDDEN)) {
     489            count = count + mds.size();
     490        }
     491        }
     492    }
     493    return count;
     494    }
     495    /** Get the value tree that matches the given element.
     496     * @param element The ElementWrapper representing the element.
     497     * @return The GValueModel representing the value tree or null.
     498     */
     499    public GValueModel getValueTree(ElementWrapper element) {
     500    GValueModel value_tree = null;
     501    if(element != null) {
     502        String namespace = element.getNamespace();
     503        if(namespace.length() > 0) {
     504        MetadataSet mds = (MetadataSet) mds_hashtable.get(namespace);
     505        if(mds != null) {
     506            value_tree = mds.getValueTree(element);
     507        }
     508        }
     509    }
     510    return value_tree;
     511    }
     512    /** This method is called to import a metadata set. First a prompt is displayed to gather necessary details such as which metadata set to import. Once this information is gathered the method <i>importMDS(File)</i> is called with the appropriate filename.
     513     * @return A boolean which is <i>true</i> if the metadata set has been imported successfully, <i>false</i> otherwise.
     514     */
     515    public boolean importMDS() {
     516    JFileChooser chooser = new JFileChooser(new File(Utility.METADATA_DIR));
     517    javax.swing.filechooser.FileFilter filter = new MDSFileFilter();
     518    chooser.setFileFilter(filter);
     519    int returnVal = chooser.showDialog(Gatherer.g_man, Gatherer.dictionary.get("MSMPrompt.File_Import"));
     520    if(returnVal == JFileChooser.APPROVE_OPTION) {
     521        return importMDS(chooser.getSelectedFile(), true);
     522    }
     523    return false;
     524    }
     525
     526    public boolean importMDS(File mds_file, boolean user_driven) {
     527    // 1. Parse the new file.
     528    MetadataSet mds_new = new MetadataSet(mds_file);
     529    // Display a prompt asking how much of the value structure the user wishes to import.
     530    if(user_driven) {
     531        ExportMDSPrompt imdsp = new ExportMDSPrompt(this, false);
     532        int result = imdsp.display();
     533        if(result == ExportMDSPrompt.EXPORT) { // Here export means the user didn't cancel.
     534        switch(imdsp.getSelectedCondition()) {
     535        case MetadataSet.NO_VALUES:
     536            mds_new = new MetadataSet(mds_new, MetadataSet.NO_VALUES);
     537            break;
     538        case MetadataSet.SUBJECTS_ONLY:
     539            mds_new = new MetadataSet(mds_new, MetadataSet.SUBJECTS_ONLY);
     540            break;
     541        default: // ALL_VALUES
     542            // Don't do anything.
     543        }
     544        }
     545        else {
     546        mds_new = null;
     547        }
     548        imdsp.dispose();
     549        imdsp = null;
     550    }
     551    // Carry on importing the new collection
     552    if(mds_new != null && mds_new.getDocument() != null) {
     553        String family = mds_new.getNamespace();
     554                // 2. See if we have another metadata set of the same family
     555                // already. If so retrieve it and merge.
     556        boolean matched = false;
     557        for(Enumeration keys = mds_hashtable.keys();
     558        keys.hasMoreElements();) {
     559        String key = (String)keys.nextElement();
     560        if(key.equals(family)) {
     561            matched = true;
     562            MetadataSet mds_cur = (MetadataSet)mds_hashtable.get(key);
     563            //ystem.err.println("Merging " + mds_new + " into " + mds_cur);
     564            mergeMDS(mds_cur, mds_new);
     565        }
     566        }
     567        if(!matched) {
     568        ///ystem.err.println("Mapping " + family + " to " + mds_new);
     569        mds_hashtable.put(family, mds_new);
     570        }
     571                // Fire setChanged() message.
     572        fireSetChanged(mds_new);
     573        return true;
     574    }
     575    // else we cancelled for some reason.
     576    return false;
     577    }
     578
     579    /** This method reloads all of the metadata sets that have been marked as included in this collection by entries in the collection configuration file.
     580     */
     581    public void load() {
     582    File source = new File(Gatherer.c_man.getCollectionMetadata());
     583    File files[] = source.listFiles();
     584    for(int i = 0; files != null && i < files.length; i++) {
     585        if(files[i].getName().endsWith(".mds")) {
     586        importMDS(files[i], false);
     587        }
     588    }
     589    // If no current 'hidden' metadata set exists, create one.
     590    if(getSet(HIDDEN) == null) {
     591        createHidden();
     592    }
     593    }
     594    /** This method takes two metadata sets, the current one and a new one, and merges them. This merge takes place at an element level falling to lower levels as necessary (using <i>mergeMDE()</i> to merge elements and <i>mergeMDV()</i> to merge value trees.
     595     * @param mds_current The currently loaded MetadataSet.
     596     * @param mds_new A new MetadataSet you wish to merge in.
     597     * @return A boolean with value <i>true</i> indicating if the merge was successful, otherwise <i>false</i> if errors were detected.
     598     */
     599    public boolean mergeMDS(MetadataSet mds_cur, MetadataSet mds_new) {
     600    // For a super quick check for equivelent trees, we compare the last changed values.
     601    if(mds_cur.getLastChanged().equals(mds_new.getLastChanged())) {
     602                // Exactly the same. Nothing to change.
     603        return true;
     604    }
     605    // Show initial progress prompt.
     606    prompt.startMerge(mds_new.size());
     607    boolean cancel = false;
     608    // For each element in the new set
     609    for(int i = 0; !cancel && i < mds_new.size(); i++) {
     610        boolean cont = false;
     611        Element mde_new = mds_new.getElement(i);
     612                // See if the element already exists in the current set
     613        Element mde_cur = mds_cur.getElement(mde_new.getAttribute("name"));
     614        int option = Declarations.NO_ACTION;
     615        while(!cont && !cancel) {
     616        // We may be dealing with a brand new element, or possibly a
     617        // renamed one.
     618        if(mde_cur == null) {
     619            // Provide merge, rename and skip options.
     620            option = prompt.mDSPrompt(mds_cur, null, mds_new, mde_new);
     621        }
     622        else {
     623            // If the two elements have equal structure we only have
     624            // to worry about merging the values.
     625            if(MSMUtils.elementsEqual(mds_cur, mde_cur, mds_new, mde_new, false)) {
     626            cancel = !mergeMDV(mds_cur, mde_cur, mds_new, mde_new);
     627            cont = true;
     628            }
     629            else {
     630            // Provide add, merge and skip options.
     631            option = prompt.mDSPrompt(mds_cur, mde_cur, mds_new, mde_new);
     632            }
     633        }
     634        String reason = null;
     635        switch(option) {
     636        case Declarations.ADD:
     637            // Only available to brand new elements, this options
     638            // simply adds the element to the set.
     639            reason = mds_cur.addElement(mde_new);
     640            if(reason == null) {
     641            cont = true;
     642            }
     643            else {
     644            prompt.addFailed(mde_new, reason);
     645            cont = false;
     646            }
     647            break;
     648        case Declarations.CANCEL:
     649            cancel = true;
     650            cont = true;
     651            break;
     652        case Declarations.FORCE_MERGE:
     653            // If the mde_cur is null, that means the users has asked
     654            // to merge but hasn't choosen any element to merge with.
     655            // Make the user select an element.
     656            mde_cur = prompt.selectElement(mds_cur);
     657        case Declarations.MERGE:
     658            // This case in turn calls the mergeMDE method to perform
     659            // the actual merging of the Elements.
     660            if(mde_cur != null) {
     661            cancel = !mergeMDE(mds_cur, mde_cur, mds_new, mde_new);
     662            }
     663            cont = true;
     664            break;
     665        case Declarations.RENAME:
     666            ///ystem.err.println("Rename element");
     667            // This case adds the Element, but requires the user to
     668            // enter a unique name.
     669            String new_name = prompt.rename(mde_new);
     670            if(new_name != null && new_name.length() > 0) {
     671            reason = mds_cur.addElement(mde_new, new_name);
     672            if(reason == null) {
     673                mde_cur = mds_cur.getElement(new_name);
     674                cont = true;
     675            }
     676            else {
     677                prompt.renameFailed(mde_new, new_name, reason);
     678                cont = false;
     679            }
     680            }
     681            else {
     682            if(new_name != null) {
     683                prompt.renameFailed(mde_new, new_name,
     684                        "MSMPrompt.Invalid_Name");
     685            }
     686            cont = false;
     687            }
     688            break;
     689        case Declarations.REPLACE:
     690            // Removes the existing Element then adds the new.
     691            mds_cur.removeElement(mde_cur);
     692            reason = mds_cur.addElement(mde_new);
     693            if(reason == null) {
     694            cont = true;
     695            }
     696            else {
     697            prompt.removeFailed(mde_cur, reason);
     698            cont = false;
     699            }
     700            break;
     701        case Declarations.SKIP:
     702            // Does not change the set.
     703            cont = true;
     704            break;
     705        }
     706        // Add this action to profile for later reference.
     707        if(profiler == null) {
     708            ///ystem.err.println("No Profiler");
     709        }
     710        //profiler.addAction(mds_new.getFile().getAbsolutePath(), MSMUtils.getFullName(mde_new), option, MSMUtils.getFullName(mde_cur));
     711        }
     712        prompt.incrementMerge();
     713    }
     714    prompt.endMerge();
     715    return true;
     716    }
     717
     718    /** This method allows for two metadata elements to be merged. Essentially merging existing elements give the users such options as keeping or replacing attribute elements as they are merged.
     719     * @param mde_cur The Element that already exists in the current metadata sets.
     720     * @param mde_new A new Element which has the same name as the current one but different data.
     721     * @return A boolean that if <i>true</i> indicats the action was completed. If <i>false</i> then an error or user action has prevented the merge.
     722     * TODO Implement
     723     */
     724    public boolean mergeMDE(MetadataSet mds_cur, Element mde_cur, MetadataSet mds_new, Element mde_new) {
     725    while(true) {
     726        for(Node mdn_new = mde_new.getFirstChild(); mdn_new != null; mdn_new = mdn_new.getNextSibling()) {
     727        // Only merge the nodes whose name is 'Attribute'
     728        if(mdn_new.getNodeName().equals("Attribute")) {
     729            Element att_new = (Element) mdn_new;
     730            // Unfortunately some attributes, such as author, can have several occurances, so match each in turn.
     731            Element temp[] = MSMUtils.getAttributeNodesNamed(mde_cur, att_new.getAttribute("name"));
     732            for(int i = 0; temp != null && i < temp.length; i++) {
     733            Element att_cur = temp[i];
     734            boolean cont = false;
     735            int action = Declarations.NO_ACTION;                     
     736            while(!cont) {
     737                if(att_cur != null) {
     738                if(!MSMUtils.attributesEqual(att_cur, att_new)) {
     739                    action = prompt.mDEPrompt(mde_cur, att_cur, mde_new, att_new);
    402740                }
    403741                else {
    404                      ///ystem.err.println("Not found.");
     742                    action = Declarations.SKIP;
    405743                }
    406                 element = null;
    407                 temp = null;
    408           }
    409           else {
    410                 ///ystem.err.println("No such set");
    411           }
    412           set = null;
    413           if(result == null) {
    414                 ///ystem.err.println("Shouldn't return null unless this is greenstone archive parsing.");
    415           }
    416           return result;
    417      }
    418      /** Retrieve a certain named element from a certain named set.
    419       * @param set The metadata set whose element you want.
    420       * @param name The name of the element.
    421       * @return An ElementWrapper around the requested element, or null if no such set or element.
    422       */
    423      public ElementWrapper getElement(String set, String name) {
    424           if(mds_hashtable.containsKey(set)) {
    425                 MetadataSet temp = (MetadataSet)mds_hashtable.get(set);
    426                 return new ElementWrapper(temp.getElement(name));
    427           }
    428           return null;
    429      }
    430      /** Get all of the metadata elements as an array of nodelists.
    431       * @return A NodeList[] of metadata elements.
    432       */
    433      public NodeList[] getNodeLists() {
    434           NodeList elements[] = null;
    435           int index = 0;
    436           elements = new NodeList[getSets().size()]; // Remember not to count hidden metadata
    437           for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) {
    438                 MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement());
    439                 if(!mds.getNamespace().equals(HIDDEN)) {
    440                      elements[index] = mds.getElements();
    441                      index++;
    442                 }
    443           }
    444           return elements;
    445      }
    446 
    447      /** Retrieve the named metadata set.
    448       * @param name The sets name as a String.
    449       * @return The MetadataSet as named, or null if no such set.
    450       */
    451      public MetadataSet getSet(String name) {
    452           if(mds_hashtable.containsKey(name)) {
    453                 return (MetadataSet) mds_hashtable.get(name);
    454           }
    455           else {
    456                 ///ystem.err.println("Couldn't find metadata set.");
    457                 if(name.equals(HIDDEN)) {
    458                      return createHidden();
    459                 }
    460           }
    461           return null;
    462      }
    463 
    464      /** Method to retrieve all of the metadata sets loaded in this collection.
    465       * @return A Vector of metadata sets.
    466       */
    467      public Vector getSets() {
    468           return getSets(true);
    469      }
    470      public Vector getSets(boolean include_greenstone) {
    471           Vector result = new Vector();
    472           for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) {
    473                 MetadataSet set = (MetadataSet)mds_hashtable.get(keys.nextElement());
    474                 if(!set.getNamespace().equals(HIDDEN) && (include_greenstone || !set.getNamespace().equals(""))) {
    475                      result.add(set);
    476                 }
    477           }
    478           return result;
    479      }
    480      /** Find the total number of elements loaded.
    481       * @return The element count as an int.
    482       */
    483      public int getSize() {
    484           int count = 0;
    485           if(mds_hashtable.size() > 0) {
    486                 for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements();) {
    487                      MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement());
    488                      if(mds.getNamespace().equals(HIDDEN)) {
    489                           count = count + mds.size();
    490                      }
    491                 }
    492           }
    493           return count;
    494      }
    495      /** Get the value tree that matches the given element.
    496       * @param element The ElementWrapper representing the element.
    497       * @return The GValueModel representing the value tree or null.
    498       */
    499      public GValueModel getValueTree(ElementWrapper element) {
    500           GValueModel value_tree = null;
    501           if(element != null) {
    502                 String namespace = element.getNamespace();
    503                 if(namespace.length() > 0) {
    504                      MetadataSet mds = (MetadataSet) mds_hashtable.get(namespace);
    505                      if(mds != null) {
    506                           value_tree = mds.getValueTree(element);
    507                      }
    508                 }
    509           }
    510           return value_tree;
    511      }
    512      /** This method is called to import a metadata set. First a prompt is displayed to gather necessary details such as which metadata set to import. Once this information is gathered the method <i>importMDS(File)</i> is called with the appropriate filename.
    513       * @return A boolean which is <i>true</i> if the metadata set has been imported successfully, <i>false</i> otherwise.
    514       */
    515      public boolean importMDS() {
    516           JFileChooser chooser = new JFileChooser(new File(Utility.METADATA_DIR));
    517           javax.swing.filechooser.FileFilter filter = new MDSFileFilter();
    518           chooser.setFileFilter(filter);
    519           int returnVal = chooser.showDialog(Gatherer.g_man, Gatherer.dictionary.get("MSMPrompt.File_Import"));
    520           if(returnVal == JFileChooser.APPROVE_OPTION) {
    521                 return importMDS(chooser.getSelectedFile(), true);
    522           }
    523           return false;
    524      }
    525 
    526      public boolean importMDS(File mds_file, boolean user_driven) {
    527           // 1. Parse the new file.
    528           MetadataSet mds_new = new MetadataSet(mds_file);
    529           // Display a prompt asking how much of the value structure the user wishes to import.
    530           if(user_driven) {
    531                 ExportMDSPrompt imdsp = new ExportMDSPrompt(this, false);
    532                 int result = imdsp.display();
    533                 if(result == ExportMDSPrompt.EXPORT) { // Here export means the user didn't cancel.
    534                      switch(imdsp.getSelectedCondition()) {
    535                      case MetadataSet.NO_VALUES:
    536                           mds_new = new MetadataSet(mds_new, MetadataSet.NO_VALUES);
    537                           break;
    538                      case MetadataSet.SUBJECTS_ONLY:
    539                           mds_new = new MetadataSet(mds_new, MetadataSet.SUBJECTS_ONLY);
    540                           break;
    541                      default: // ALL_VALUES
    542                           // Don't do anything.
    543                      }
    544                 }
    545                 else {
    546                      mds_new = null;
    547                 }
    548                 imdsp.dispose();
    549                 imdsp = null;
    550           }
    551           // Carry on importing the new collection
    552           if(mds_new != null && mds_new.getDocument() != null) {
    553                 String family = mds_new.getNamespace();
    554                 // 2. See if we have another metadata set of the same family
    555                 // already. If so retrieve it and merge.
    556                 boolean matched = false;
    557                 for(Enumeration keys = mds_hashtable.keys();
    558                      keys.hasMoreElements();) {
    559                      String key = (String)keys.nextElement();
    560                      if(key.equals(family)) {
    561                           matched = true;
    562                           MetadataSet mds_cur = (MetadataSet)mds_hashtable.get(key);
    563                           //ystem.err.println("Merging " + mds_new + " into " + mds_cur);
    564                           mergeMDS(mds_cur, mds_new);
    565                      }
    566                 }
    567                 if(!matched) {
    568                      ///ystem.err.println("Mapping " + family + " to " + mds_new);
    569                      mds_hashtable.put(family, mds_new);
    570                 }
    571                 // Fire setChanged() message.
    572                 fireSetChanged(mds_new);
    573                 return true;
    574           }
    575           // else we cancelled for some reason.
    576           return false;
    577      }
    578 
    579      /** This method reloads all of the metadata sets that have been marked as included in this collection by entries in the collection configuration file.
    580       */
    581      public void load() {
    582           File source = new File(Gatherer.c_man.getCollectionMetadata());
    583           File files[] = source.listFiles();
    584           for(int i = 0; files != null && i < files.length; i++) {
    585                 if(files[i].getName().endsWith(".mds")) {
    586                      importMDS(files[i], false);
    587                 }
    588           }
    589           // If no current 'hidden' metadata set exists, create one.
    590           if(getSet(HIDDEN) == null) {
    591                 createHidden();
    592           }
    593      }
    594      /** This method takes two metadata sets, the current one and a new one, and merges them. This merge takes place at an element level falling to lower levels as necessary (using <i>mergeMDE()</i> to merge elements and <i>mergeMDV()</i> to merge value trees.
    595       * @param mds_current The currently loaded MetadataSet.
    596       * @param mds_new A new MetadataSet you wish to merge in.
    597       * @return A boolean with value <i>true</i> indicating if the merge was successful, otherwise <i>false</i> if errors were detected.
    598       */
    599      public boolean mergeMDS(MetadataSet mds_cur, MetadataSet mds_new) {
    600           // For a super quick check for equivelent trees, we compare the last changed values.
    601           if(mds_cur.getLastChanged().equals(mds_new.getLastChanged())) {
    602                 // Exactly the same. Nothing to change.
    603                 return true;
    604           }
    605           // Show initial progress prompt.
    606           prompt.startMerge(mds_new.size());
    607           boolean cancel = false;
    608           // For each element in the new set
    609           for(int i = 0; !cancel && i < mds_new.size(); i++) {
    610                 boolean cont = false;
    611                 Element mde_new = mds_new.getElement(i);
    612                 // See if the element already exists in the current set
    613                 Element mde_cur = mds_cur.getElement(mde_new.getAttribute("name"));
    614                 int option = Declarations.NO_ACTION;
    615                 while(!cont && !cancel) {
    616                      // We may be dealing with a brand new element, or possibly a
    617                      // renamed one.
    618                      if(mde_cur == null) {
    619                           // Provide merge, rename and skip options.
    620                           option = prompt.mDSPrompt(mds_cur, null, mds_new, mde_new);
    621                      }
    622                      else {
    623                           // If the two elements have equal structure we only have
    624                           // to worry about merging the values.
    625                           if(MSMUtils.elementsEqual(mds_cur, mde_cur, mds_new, mde_new, false)) {
    626                                 cancel = !mergeMDV(mds_cur, mde_cur, mds_new, mde_new);
    627                                 cont = true;
    628                           }
    629                           else {
    630                                 // Provide add, merge and skip options.
    631                                 option = prompt.mDSPrompt(mds_cur, mde_cur, mds_new, mde_new);
    632                           }
    633                      }
    634                      String reason = null;
    635                      switch(option) {
    636                      case Declarations.ADD:
    637                           // Only available to brand new elements, this options
    638                           // simply adds the element to the set.
    639                           reason = mds_cur.addElement(mde_new);
    640                           if(reason == null) {
    641                                 cont = true;
    642                           }
    643                           else {
    644                                 prompt.addFailed(mde_new, reason);
    645                                 cont = false;
    646                           }
    647                           break;
    648                      case Declarations.CANCEL:
    649                           cancel = true;
    650                           cont = true;
    651                           break;
    652                      case Declarations.FORCE_MERGE:
    653                           // If the mde_cur is null, that means the users has asked
    654                           // to merge but hasn't choosen any element to merge with.
    655                           // Make the user select an element.
    656                           mde_cur = prompt.selectElement(mds_cur);
    657                      case Declarations.MERGE:
    658                           // This case in turn calls the mergeMDE method to perform
    659                           // the actual merging of the Elements.
    660                           if(mde_cur != null) {
    661                                 cancel = !mergeMDE(mds_cur, mde_cur, mds_new, mde_new);
    662                           }
    663                           cont = true;
    664                           break;
    665                      case Declarations.RENAME:
    666                           ///ystem.err.println("Rename element");
    667                           // This case adds the Element, but requires the user to
    668                           // enter a unique name.
    669                           String new_name = prompt.rename(mde_new);
    670                           if(new_name != null && new_name.length() > 0) {
    671                                 reason = mds_cur.addElement(mde_new, new_name);
    672                                 if(reason == null) {
    673                                      mde_cur = mds_cur.getElement(new_name);
    674                                      cont = true;
    675                                 }
    676                                 else {
    677                                      prompt.renameFailed(mde_new, new_name, reason);
    678                                      cont = false;
    679                                 }
    680                           }
    681                           else {
    682                                 if(new_name != null) {
    683                                      prompt.renameFailed(mde_new, new_name,
    684                                                                 "MSMPrompt.Invalid_Name");
    685                                 }
    686                                 cont = false;
    687                           }
    688                           break;
    689                      case Declarations.REPLACE:
    690                           // Removes the existing Element then adds the new.
    691                           mds_cur.removeElement(mde_cur);
    692                           reason = mds_cur.addElement(mde_new);
    693                           if(reason == null) {
    694                                 cont = true;
    695                           }
    696                           else {
    697                                 prompt.removeFailed(mde_cur, reason);
    698                                 cont = false;
    699                           }
    700                           break;
    701                      case Declarations.SKIP:
    702                           // Does not change the set.
    703                           cont = true;
    704                           break;
    705                      }
    706                      // Add this action to profile for later reference.
    707                      if(profiler == null) {
    708                           ///ystem.err.println("No Profiler");
    709                      }
    710                      //profiler.addAction(mds_new.getFile().getAbsolutePath(), MSMUtils.getFullName(mde_new), option, MSMUtils.getFullName(mde_cur));
    711                 }
    712                 prompt.incrementMerge();
    713           }
    714           prompt.endMerge();
    715           return true;
    716      }
    717 
    718      /** This method allows for two metadata elements to be merged. Essentially merging existing elements give the users such options as keeping or replacing attribute elements as they are merged.
    719       * @param mde_cur The Element that already exists in the current metadata sets.
    720       * @param mde_new A new Element which has the same name as the current one but different data.
    721       * @return A boolean that if <i>true</i> indicats the action was completed. If <i>false</i> then an error or user action has prevented the merge.
    722       * TODO Implement
    723       */
    724      public boolean mergeMDE(MetadataSet mds_cur, Element mde_cur, MetadataSet mds_new, Element mde_new) {
    725           while(true) {
    726                 for(Node mdn_new = mde_new.getFirstChild(); mdn_new != null; mdn_new = mdn_new.getNextSibling()) {
    727                      // Only merge the nodes whose name is 'Attribute'
    728                      if(mdn_new.getNodeName().equals("Attribute")) {
    729                           Element att_new = (Element) mdn_new;
    730                           // Unfortunately some attributes, such as author, can have several occurances, so match each in turn.
    731                           Element temp[] = MSMUtils.getAttributeNodesNamed(mde_cur, att_new.getAttribute("name"));
    732                           for(int i = 0; temp != null && i < temp.length; i++) {
    733                                 Element att_cur = temp[i];
    734                                 boolean cont = false;
    735                                 int action = Declarations.NO_ACTION;                     
    736                                 while(!cont) {
    737                                      if(att_cur != null) {
    738                                           if(!MSMUtils.attributesEqual(att_cur, att_new)) {
    739                                                 action = prompt.mDEPrompt(mde_cur, att_cur, mde_new, att_new);
    740                                           }
    741                                           else {
    742                                                 action = Declarations.SKIP;
    743                                           }
    744                                      }
    745                                      else {
    746                                           action = Declarations.ADD;
    747                                      }
    748                                      switch (action) {
    749                                      case Declarations.REPLACE:
    750                                           // Out with the old.
    751                                           mde_cur.removeChild(att_cur);
    752                                      case Declarations.ADD:
    753                                           // Simply add the new attribute. No clash is possible as we have already tested for it.
    754                                           MSMUtils.add(mde_cur, att_new);
    755                                           cont = true;
    756                                           break;
    757                                      case Declarations.SKIP:
    758                                           // Do nothing. Move on to next attribute.
    759                                           cont = true;
    760                                           break;
    761                                      case Declarations.CANCEL:
    762                                           return false;
    763                                      default:
    764                                           cont = false;
    765                                      }
    766                                 }
    767                                 att_cur = null;
    768                           }
    769                           temp = null;
    770                           att_new = null;
    771                      }
    772                 }
    773                 return mergeMDV(mds_cur, mde_cur, mds_new, mde_new);
    774           }
    775      }
    776      /** Merge two metadata value trees.
    777       * @param mds_cur The current MetadataSet.
    778       * @param mde_cur The current Element which acts as a value tree root.
    779       * @param mds_new The MetadataSet we are merging in.
    780       * @param mde_new The Element which acts as a value tree that we are merging in.
    781       */
    782      public boolean mergeMDV(MetadataSet mds_cur, Element mde_cur,
    783                                      MetadataSet mds_new, Element mde_new) {
    784           // Remember we may be asked to merge with a current mdv of null.
    785           return MSMUtils.updateValueTree(mds_cur, mde_cur, mds_new, mde_new);
    786      }
    787 
    788      public void removeElement(ElementWrapper element) {
    789           // Retrieve the metadata set this element belongs to.
    790           String namespace = element.getNamespace();
    791           MetadataSet set = (MetadataSet) mds_hashtable.get(namespace);
    792           if(set != null) {
     744                }
     745                else {
     746                action = Declarations.ADD;
     747                }
     748                switch (action) {
     749                case Declarations.REPLACE:
     750                // Out with the old.
     751                mde_cur.removeChild(att_cur);
     752                case Declarations.ADD:
     753                // Simply add the new attribute. No clash is possible as we have already tested for it.
     754                MSMUtils.add(mde_cur, att_new);
     755                cont = true;
     756                break;
     757                case Declarations.SKIP:
     758                // Do nothing. Move on to next attribute.
     759                cont = true;
     760                break;
     761                case Declarations.CANCEL:
     762                return false;
     763                default:
     764                cont = false;
     765                }
     766            }
     767            att_cur = null;
     768            }
     769            temp = null;
     770            att_new = null;
     771        }
     772        }
     773        return mergeMDV(mds_cur, mde_cur, mds_new, mde_new);
     774    }
     775    }
     776    /** Merge two metadata value trees.
     777     * @param mds_cur The current MetadataSet.
     778     * @param mde_cur The current Element which acts as a value tree root.
     779     * @param mds_new The MetadataSet we are merging in.
     780     * @param mde_new The Element which acts as a value tree that we are merging in.
     781     */
     782    public boolean mergeMDV(MetadataSet mds_cur, Element mde_cur,
     783                MetadataSet mds_new, Element mde_new) {
     784    // Remember we may be asked to merge with a current mdv of null.
     785    return MSMUtils.updateValueTree(mds_cur, mde_cur, mds_new, mde_new);
     786    }
     787
     788    public void removeElement(ElementWrapper element) {
     789    // Retrieve the metadata set this element belongs to.
     790    String namespace = element.getNamespace();
     791    MetadataSet set = (MetadataSet) mds_hashtable.get(namespace);
     792    if(set != null) {
    793793                // Bugger. Get the old name -before- we remove the element from the set.
    794                 String old_name = element.toString();
     794        String old_name = element.toString();
    795795                // Remove the element.
    796                 set.removeElement(element.getElement());
     796        set.removeElement(element.getElement());
    797797                // Fire event.
    798                 fireElementChanged(new MSMEvent(this, null, old_name));
    799           }
    800           else {
     798        fireElementChanged(new MSMEvent(this, null, old_name));
     799    }
     800    else {
    801801                ///ystem.err.println("no such set " + namespace);
    802           }
    803           // No such set. No such element.
    804     }
     802    }
     803    // No such set. No such element.
     804    }
    805805     
    806     /** Remove a piece of metadata from a record or records[] and fire all the relevant events.
    807       * @param records A FileNode[] of records to be changed.
    808       * @param metadata The Metadata to remove.
    809       */
    810     public void removeMetadata(long id, Metadata metadata, FileNode records[]) {
    811           // Reset undo buffer
    812           undo_buffer.clear();
    813           // Simplier than the others. Simply remove the metadata.
    814           int action = MetaEditPrompt.CONFIRM;
    815           // Now remove metadata from the selected file nodes.
    816           for(int i = 0; action != MetaEditPrompt.CANCEL && i < records.length; i++) {
    817                 action = removeMetadata(id, records[i], metadata, action, (records.length > 1));
    818           }
    819           // If we were cancelled we should undo any changes so far
    820           if(action == MetaEditPrompt.CANCEL) {
    821                 for(Iterator keys = undo_buffer.keySet().iterator(); keys.hasNext(); ) {
    822                      FileNode record = (FileNode) keys.next();
    823                      undoRemove(id, record);
    824                 }
    825           }
    826     }
    827     /** Remove the specified listener from ourselves.
    828       * @param listener The MSMListener in question.
    829       */
    830     public void removeMSMListener(MSMListener listener) {
    831           listeners.remove(listener);
    832     }
    833 
    834     public void removeSet(MetadataSet set) {
    835           mds_hashtable.remove(set.getNamespace());
    836           fireSetChanged(set);
    837     }
    838 
    839     /** Rename the identifier of a given element to the name given.
    840       * @param element The metadata element effected, as an ElementWrapper.
    841       * @param new_name The String to use as the new name.
    842       */
    843     public void renameElement(ElementWrapper element, String new_name) {
    844           Element e = element.getElement();
    845           String old_name = element.toString();
    846           MSMUtils.setIdentifier(e, new_name);
    847           fireElementChanged(new MSMEvent(this, element, old_name));
    848           old_name = null;
    849           e = null;
    850     }
    851     /** A method to save the state of this metadata set manager. First we ensure that the names of all included metadata sets have been added to the collection configuration file, then all of the metadata sets contained are exported with full content to the collect/<col_name>/metadata/ directory.
    852       */
    853     public void save() {
    854           // Create the correct file to save these sets to...
    855           File file = new File(Gatherer.self.getCollectionMetadata());
    856           // And make back ups of all existing metadata set files.
    857           File temp[] = file.listFiles();
    858           for(int i = temp.length - 1; i >= 0; i--) {
    859                 if(temp[i].getName().endsWith(".mds") || temp[i].getName().endsWith(".mdv")) {
    860                      File backup = new File(temp[i].getAbsolutePath() + "~");
    861                      backup.deleteOnExit();
    862                      if(!temp[i].renameTo(backup)) {
    863                           Gatherer.println("Error in MetadataSetManager.save(): FileRenameException");
    864                      }
    865                 }
    866           }
    867           // Now save the latest versions of the metadata sets.
    868           for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) {
    869                 String namespace = (String) keys.nextElement();
    870                 MetadataSet set = (MetadataSet) mds_hashtable.get(namespace);
    871                 try {
    872                      File mds_file = null;
    873                      if(!namespace.equals("")) {
    874                           mds_file = new File(file, set.getNamespace() + ".mds");
    875                      }
    876                      else {
    877                           mds_file = new File(file, "greenstone.mds");
    878                      }
    879                      Utility.export(set.getDocument(), mds_file);
    880                      set.setFile(mds_file);
    881                      // Now for each element attempt to save its value tree.
    882                      NodeList elements = set.getElements();
    883                      for(int i = elements.getLength() - 1;  !namespace.equals("") && i >= 0; i--) {
    884                           ElementWrapper value_element = new ElementWrapper((Element)elements.item(i));
    885                           GValueModel value_tree = set.getValueTree(value_element);
    886                           if(value_tree != null) {
    887                                 File value_file = new File(file, value_element.toString() + ".mdv");
    888                                 ///ystem.err.println("Saving value file: " + value_file.toString());
    889                                 Utility.export(value_tree.getDocument(), value_file);
    890                                 // If this is a hierarchy element, write hierarchy file.
    891                                 if(value_element.getNamespace().equals(MetadataSetManager.HIDDEN) || value_tree.isHierarchy()) {
    892                                     MetadataXML.write(value_tree, this, Gatherer.c_man.getCollectionEtc());
    893                                 }
    894                           }
    895                           else {
    896                                 ///ystem.err.println("No value tree for " + value_element.toString());
    897                           }
    898                      }
    899                      // Note that filenames might have changed so we better warn everyone.
    900                      fireSetChanged(set);
    901                 }
    902                 catch (Exception error) {
    903                      error.printStackTrace();
    904                 }
    905           }
    906           profiler.save();
    907     }
    908     /** Given a FileNode of the original file and the new FileNode, search for any metadata, either from a greenstone directory archive xml file, or from one of the registered 'plugin' parsers.
    909       * @param source The source FileNode.
    910       * @param destination The new FileNode.
    911       */
    912     public final boolean searchForMetadata(FileNode destination, FileNode source, boolean folder_level) {
    913           return searchForMetadata(destination, source, folder_level, false);
    914     }
    915     public final boolean searchForMetadata(FileNode destination, FileNode source, boolean folder_level, boolean dummy_run) {
    916           ///atherer.println("MetadataSetManager.searchForMetadata()");
    917           return loader.searchForMetadata(destination, source, folder_level, dummy_run);
    918     }
    919     /** Build a vector of all the metadata sets that contain an element with the given name.
    920       * @param name The name of an element as a String.
    921       * @return A Vector of metadata sets.
    922       * @see MSMPrompt (org.greenstone.gatherer.msm.MSMPrompt#selectSet)
    923       */
    924     public Vector setsThatContain(String name) {
    925           Vector result = new Vector();
    926           for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) {
    927                 MetadataSet set = (MetadataSet) mds_hashtable.get(keys.nextElement());
    928                 if(set.getElement(name) != null) {
    929                      result.add(set);
    930                 }
    931           }
    932           return result;
    933     }
    934 
    935     public final int size() {
    936           return getSets().size();
    937     }
    938 
    939     /** Update a piece of metadata connected to a record or records, ensuring the value tree is built properly, and correct messaging fired.
    940       * @param records A FileNode[] of records, or directories, to add the specified metadata to.
    941       * @param element The metadata element, contained within an ElementWrapper to base metadata on.
    942       * @param value The value to assign to the metadata as a String.
    943       * @param action The default action to take in the prompt.
    944       * @param file_level If true then the metadata can be replaced normally, if false then we should actually use an add method instead.
    945       * @return The Metadata we just assigned.
    946       */
    947     public Metadata updateMetadata(long id, Metadata old_metadata, FileNode records[], String value_str, int action, boolean file_level) {
    948           // Retrieve the new value node from the same value tree as the old metadata.
    949           ElementWrapper element = old_metadata.getElement();
    950           GValueModel model = getValueTree(element);
    951           GValueNode value = null;
    952           if(model != null) {
    953                 value = model.addValue(value_str);
    954           }
    955           else {
    956                 value = new GValueNode(element.toString(), value_str);
    957           }
    958           // Create new metadata.
    959           Metadata new_metadata = new Metadata(value);
    960           // Reset the undo buffer
    961           undo_buffer.clear();
    962           // And update the old with it.
    963           if(action == -1) {
    964                 action = MetaEditPrompt.CONFIRM;
    965           }
    966           // And then update each selection file node.
    967           for(int i = 0; action != MetaEditPrompt.CANCEL && i < records.length; i++) {
    968                 action = updateMetadata(id, records[i], old_metadata, new_metadata, action, (records.length > 1), file_level);
    969           }
    970           // If we were cancelled we should undo any changes so far
    971           if(action == MetaEditPrompt.CANCEL) {
    972                 for(Iterator keys = undo_buffer.keySet().iterator(); keys.hasNext(); ) {
    973                      FileNode record = (FileNode) keys.next();
    974                      undoUpdate(id, record);
    975                 }
    976           }
    977           // All done. Any events would have been fired within the record recursion.
    978           return new_metadata;
    979     }
    980 
    981     /** Add a reference to a piece of metadata to the given FileNode. The whole method gets a wee bit messy as we have to allow for several different commands from users such as accumulate / overwrite, skip just this file or cancel the whole batch. Cancelling is especially problematic as we need to rollback any changes (within reason).
    982       * It is also worth mentioning that despite its name, no actual metadata is added directly by this method. Instead a call to fireMetadataChanged() is issued, which is in turn processed by the GDMManager (which, given this method may have been called from GDMManager as well, means the cycle is complete. Um, that doesn't mean theres an infinite loop... I hope).
    983       * @param id a long unique identifier shared by all actions caused by the same gesture.
    984       * @param record the FileNode we are adding the metadata to.
    985       * @param data the new Metadata.
    986       * @param action the default action as an int. May require user interaction.
    987       * @param fire_event <i>true</i> if this action should fire a metadata changed event, <i>false</i> if we are calling this as an affect of a previous event. (Don't want an infinitely recursive loop, do we).
    988       * @param multiple_selection <i>true</i> if more than one file or folder was selected.
    989       * @return an int specifying the current action. Thus changes in lower parts of the tree continue to effect other disjoint subtrees.
    990       */
    991     private int addMetadata(long id, FileNode record, Metadata data, int action, boolean multiple_selection) {
    992           // Super special exception for accumulate all action. We are going to add this metadata anyway, regardless of whats already there, so just add it.
    993           if(action == MetaEditPrompt.ACCUMULATE_ALL || action == MetaEditPrompt.OVERWRITE_ALL) {
    994                 fireMetadataChanged(id, record, null, data);
    995           }
    996           else {
     806    /** Remove a piece of metadata from a record or records[] and fire all the relevant events.
     807     * @param records A FileNode[] of records to be changed.
     808     * @param metadata The Metadata to remove.
     809     */
     810    public void removeMetadata(long id, Metadata metadata, FileNode records[]) {
     811    // Reset undo buffer
     812    undo_buffer.clear();
     813    // Simplier than the others. Simply remove the metadata.
     814    int action = MetaEditPrompt.CONFIRM;
     815    // Now remove metadata from the selected file nodes.
     816    for(int i = 0; action != MetaEditPrompt.CANCEL && i < records.length; i++) {
     817        action = removeMetadata(id, records[i], metadata, action, (records.length > 1));
     818    }
     819    // If we were cancelled we should undo any changes so far
     820    if(action == MetaEditPrompt.CANCEL) {
     821        for(Iterator keys = undo_buffer.keySet().iterator(); keys.hasNext(); ) {
     822        FileNode record = (FileNode) keys.next();
     823        undoRemove(id, record);
     824        }
     825    }
     826    }
     827    /** Remove the specified listener from ourselves.
     828     * @param listener The MSMListener in question.
     829     */
     830    public void removeMSMListener(MSMListener listener) {
     831    listeners.remove(listener);
     832    }
     833
     834    public void removeSet(MetadataSet set) {
     835    mds_hashtable.remove(set.getNamespace());
     836    fireSetChanged(set);
     837    }
     838
     839    /** Rename the identifier of a given element to the name given.
     840     * @param element The metadata element effected, as an ElementWrapper.
     841     * @param new_name The String to use as the new name.
     842     */
     843    public void renameElement(ElementWrapper element, String new_name) {
     844    Element e = element.getElement();
     845    String old_name = element.toString();
     846    MSMUtils.setIdentifier(e, new_name);
     847    fireElementChanged(new MSMEvent(this, element, old_name));
     848    old_name = null;
     849    e = null;
     850    }
     851    /** A method to save the state of this metadata set manager. First we ensure that the names of all included metadata sets have been added to the collection configuration file, then all of the metadata sets contained are exported with full content to the collect/<col_name>/metadata/ directory.
     852     */
     853    public void save() {
     854    // Create the correct file to save these sets to...
     855    File file = new File(Gatherer.self.getCollectionMetadata());
     856    // And make back ups of all existing metadata set files.
     857    File temp[] = file.listFiles();
     858    for(int i = temp.length - 1; i >= 0; i--) {
     859        if(temp[i].getName().endsWith(".mds") || temp[i].getName().endsWith(".mdv")) {
     860        File backup = new File(temp[i].getAbsolutePath() + "~");
     861        backup.deleteOnExit();
     862        if(!temp[i].renameTo(backup)) {
     863            Gatherer.println("Error in MetadataSetManager.save(): FileRenameException");
     864        }
     865        }
     866    }
     867    // Now save the latest versions of the metadata sets.
     868    for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) {
     869        String namespace = (String) keys.nextElement();
     870        MetadataSet set = (MetadataSet) mds_hashtable.get(namespace);
     871        try {
     872        File mds_file = null;
     873        if(!namespace.equals("")) {
     874            mds_file = new File(file, set.getNamespace() + ".mds");
     875        }
     876        else {
     877            mds_file = new File(file, "greenstone.mds");
     878        }
     879        Utility.export(set.getDocument(), mds_file);
     880        set.setFile(mds_file);
     881        // Now for each element attempt to save its value tree.
     882        NodeList elements = set.getElements();
     883        for(int i = elements.getLength() - 1;   !namespace.equals("") && i >= 0; i--) {
     884            ElementWrapper value_element = new ElementWrapper((Element)elements.item(i));
     885            GValueModel value_tree = set.getValueTree(value_element);
     886            if(value_tree != null) {
     887            File value_file = new File(file, value_element.toString() + ".mdv");
     888            ///ystem.err.println("Saving value file: " + value_file.toString());
     889            Utility.export(value_tree.getDocument(), value_file);
     890            // If this is a hierarchy element, write hierarchy file.
     891            if(value_element.getNamespace().equals(MetadataSetManager.HIDDEN) || value_tree.isHierarchy()) {
     892                MetadataXML.write(value_tree, this, Gatherer.c_man.getCollectionEtc());
     893            }
     894            }
     895            else {
     896            ///ystem.err.println("No value tree for " + value_element.toString());
     897            }
     898        }
     899        // Note that filenames might have changed so we better warn everyone.
     900        fireSetChanged(set);
     901        }
     902        catch (Exception error) {
     903        error.printStackTrace();
     904        }
     905    }
     906    profiler.save();
     907    }
     908    /** Given a FileNode of the original file and the new FileNode, search for any metadata, either from a greenstone directory archive xml file, or from one of the registered 'plugin' parsers.
     909     * @param source The source FileNode.
     910     * @param destination The new FileNode.
     911     */
     912    public final boolean searchForMetadata(FileNode destination, FileNode source, boolean folder_level) {
     913    return searchForMetadata(destination, source, folder_level, false);
     914    }
     915    public final boolean searchForMetadata(FileNode destination, FileNode source, boolean folder_level, boolean dummy_run) {
     916    ///atherer.println("MetadataSetManager.searchForMetadata()");
     917    return loader.searchForMetadata(destination, source, folder_level, dummy_run);
     918    }
     919    /** Build a vector of all the metadata sets that contain an element with the given name.
     920     * @param name The name of an element as a String.
     921     * @return A Vector of metadata sets.
     922     * @see MSMPrompt (org.greenstone.gatherer.msm.MSMPrompt#selectSet)
     923     */
     924    public Vector setsThatContain(String name) {
     925    Vector result = new Vector();
     926    for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) {
     927        MetadataSet set = (MetadataSet) mds_hashtable.get(keys.nextElement());
     928        if(set.getElement(name) != null) {
     929        result.add(set);
     930        }
     931    }
     932    return result;
     933    }
     934
     935    public final int size() {
     936    return getSets().size();
     937    }
     938
     939    /** Update a piece of metadata connected to a record or records, ensuring the value tree is built properly, and correct messaging fired.
     940     * @param records A FileNode[] of records, or directories, to add the specified metadata to.
     941     * @param element The metadata element, contained within an ElementWrapper to base metadata on.
     942     * @param value The value to assign to the metadata as a String.
     943     * @param action The default action to take in the prompt.
     944     * @param file_level If true then the metadata can be replaced normally, if false then we should actually use an add method instead.
     945     * @return The Metadata we just assigned.
     946     */
     947    public Metadata updateMetadata(long id, Metadata old_metadata, FileNode records[], String value_str, int action, boolean file_level) {
     948    // Retrieve the new value node from the same value tree as the old metadata.
     949    ElementWrapper element = old_metadata.getElement();
     950    GValueModel model = getValueTree(element);
     951    GValueNode value = null;
     952    if(model != null) {
     953        value = model.addValue(value_str);
     954    }
     955    else {
     956        value = new GValueNode(element.toString(), value_str);
     957    }
     958    // Create new metadata.
     959    Metadata new_metadata = new Metadata(value);
     960    // Reset the undo buffer
     961    undo_buffer.clear();
     962    // And update the old with it.
     963    if(action == -1) {
     964        action = MetaEditPrompt.CONFIRM;
     965    }
     966    // And then update each selection file node.
     967    for(int i = 0; action != MetaEditPrompt.CANCEL && i < records.length; i++) {
     968        action = updateMetadata(id, records[i], old_metadata, new_metadata, action, (records.length > 1), file_level);
     969    }
     970    // If we were cancelled we should undo any changes so far
     971    if(action == MetaEditPrompt.CANCEL) {
     972        for(Iterator keys = undo_buffer.keySet().iterator(); keys.hasNext(); ) {
     973        FileNode record = (FileNode) keys.next();
     974        undoUpdate(id, record);
     975        }
     976    }
     977    // All done. Any events would have been fired within the record recursion.
     978    return new_metadata;
     979    }
     980
     981    /** Add a reference to a piece of metadata to the given FileNode. The whole method gets a wee bit messy as we have to allow for several different commands from users such as accumulate / overwrite, skip just this file or cancel the whole batch. Cancelling is especially problematic as we need to rollback any changes (within reason).
     982     * It is also worth mentioning that despite its name, no actual metadata is added directly by this method. Instead a call to fireMetadataChanged() is issued, which is in turn processed by the GDMManager (which, given this method may have been called from GDMManager as well, means the cycle is complete. Um, that doesn't mean theres an infinite loop... I hope).
     983     * @param id a long unique identifier shared by all actions caused by the same gesture.
     984     * @param record the FileNode we are adding the metadata to.
     985     * @param data the new Metadata.
     986     * @param action the default action as an int. May require user interaction.
     987     * @param fire_event <i>true</i> if this action should fire a metadata changed event, <i>false</i> if we are calling this as an affect of a previous event. (Don't want an infinitely recursive loop, do we).
     988     * @param multiple_selection <i>true</i> if more than one file or folder was selected.
     989     * @return an int specifying the current action. Thus changes in lower parts of the tree continue to effect other disjoint subtrees.
     990     */
     991    private int addMetadata(long id, FileNode record, Metadata data, int action, boolean multiple_selection) {
     992    // Super special exception for accumulate all action. We are going to add this metadata anyway, regardless of whats already there, so just add it.
     993    if(action == MetaEditPrompt.ACCUMULATE_ALL || action == MetaEditPrompt.OVERWRITE_ALL) {
     994        fireMetadataChanged(id, record, null, data);
     995    }
     996    else {
    997997                // Recover the metadata from this file.
    998                 ArrayList metadata = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile());
     998        ArrayList metadata = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile());
    999999                // Most important test, we don't have to add the metadata if its already there!
    1000                 if(!metadata.contains(data)) {
    1001                      // Record undo information for this file node.
    1002                      ArrayList undo = new ArrayList();
    1003                      // Prepare for MEP
    1004                      int user_action = MetaEditPrompt.ACCUMULATE;
    1005                      String values = "";
    1006                      // See if there is any existing metadata with the same name. If so make a string from all the values (bob, jim etc).
    1007                      int metadata_size = metadata.size();
    1008                      for(int i = 0; i < metadata_size; i++) {
    1009                           Metadata current_data = (Metadata)metadata.get(i);
    1010                           if(current_data.getElement().equals(data.getElement())) {
    1011                                 if(values.length() > 0) {
    1012                                     values = values + ", ";
    1013                                 }
    1014                                 values = values + current_data.getValue();
    1015                           }
    1016                      }
    1017                      // If we are confirming prompt for user_action.
    1018                      if(values.length() > 0 && action == MetaEditPrompt.CONFIRM) {
    1019                           MetaEditPrompt mep = new MetaEditPrompt(MetaEditPrompt.ADD_PROMPT, multiple_selection, record.getFile(), data.getElement().toString(), values, data.getValue());
    1020                           user_action = mep.display();
    1021                      }
    1022                      if(user_action == MetaEditPrompt.ACCUMULATE_ALL || user_action == MetaEditPrompt.CANCEL || user_action == MetaEditPrompt.OVERWRITE_ALL) {
    1023                           action = user_action;
    1024                      }
    1025                      // If we are overwriting we first remove all metadata with the same element, unless the metadata is non-file level is which case we leave it, and hope the accumulate vs overwrite will be followed during the determining of metadata assigned.
    1026                      if(action == MetaEditPrompt.OVERWRITE_ALL || user_action == MetaEditPrompt.OVERWRITE) {
    1027                           for(int i = metadata_size; i != 0; i--) {
    1028                                 Metadata old_data = (Metadata)metadata.get(i - 1);
    1029                                 if(old_data.getElement().equals(data.getElement()) && old_data.isFileLevel()) {
    1030                                     // We have a match. Remove this metadata.
    1031                                     fireMetadataChanged(id, record, old_data, null);
    1032                                     // Add it to our undo buffer.
    1033                                     undo.add(old_data);
    1034                                 }
    1035                           }
    1036                      }
    1037                      // Ensure the metadata will accumulate or overwrite as the user wishes.
    1038                      if(user_action == MetaEditPrompt.ACCUMULATE || user_action == MetaEditPrompt.ACCUMULATE_ALL) {
    1039                           data.setAccumulate(true);
    1040                      }
    1041                      else if(user_action == MetaEditPrompt.OVERWRITE || user_action == MetaEditPrompt.OVERWRITE_ALL) {
    1042                           data.setAccumulate(false);
    1043                      }
    1044                      // Unless cancelled, add the metadata after checking we don't already have it in the metadata (obviously not if we're overwriting but we'd better check anyway). Also if we've skipped the file we should do so, but move on to the next child...
    1045                      if((user_action == MetaEditPrompt.ACCUMULATE || user_action == MetaEditPrompt.ACCUMULATE_ALL || user_action == MetaEditPrompt.OVERWRITE || user_action == MetaEditPrompt.OVERWRITE_ALL) && !metadata.contains(data)) {
    1046                           ///ystem.err.println("Adding metadata " + data);
    1047                           fireMetadataChanged(id, record, null, data);
    1048                           // The last element in undo is the new element.
    1049                           undo.add(data);
    1050                      }
    1051                      // Store the undo list in our undo buffer.
    1052                      undo_buffer.put(record, undo);
    1053                 }
    1054           }
    1055           // If we've been cancelled, roll back the addition.
    1056           if(action == MetaEditPrompt.CANCEL) {
    1057                 undoAdd(id, record);
    1058           }
    1059           return action;
    1060     }
    1061     /** addMetadata(long, FileNode, Metadata, int, boolean) */
    1062 
    1063     /** Create the hidden mds, used for custom classifiers. */
    1064     private MetadataSet createHidden() {
    1065           MetadataSet hidden_mds = new MetadataSet(Utility.METADATA_SET_TEMPLATE);
    1066           hidden_mds.setAttribute("creator","The Gatherer");
    1067           hidden_mds.setAttribute("contact","gatherer@greenstone");
    1068           hidden_mds.setAttribute("description","A hidden metadata set used to create custom classifiers.");
    1069           hidden_mds.setAttribute("family","Gatherer Hidden Metadata");
    1070           hidden_mds.setAttribute("lastchanged","");
    1071           hidden_mds.setAttribute("name","Gatherer Hidden Metadata");
    1072           hidden_mds.setAttribute("namespace",HIDDEN);
    1073           mds_hashtable.put(HIDDEN, hidden_mds);
    1074           fireSetChanged(hidden_mds);
    1075           return hidden_mds;
    1076     }
    1077 
    1078     /** Creates a new profiler, which in turn will attempt to load previous profile information. */
    1079     private void loadProfiler() {
    1080           profiler = new MSMProfiler();
    1081           addMSMListener(profiler);
    1082     }
    1083 
    1084     /** In order to remove metadata from the tree you first call this method providing it with the metadata you want removed. This will remove any occurance of said metadata from the given FileNode (using fireMetadataChanged()).
    1085       * @param id a unique long identifier common to all actions caused by a single gesture.
    1086       * @param record the FileNode who we are removing metadata from.
    1087       * @param data the <strong>Metadata</strong> you wish removed from the tree.
    1088       * @param action an <i>int</i> specifying the wanted prompting action.
    1089       * @param fire_event <i>true</i> if this action should fire metadata changed events.
    1090       * @param multiple_selection the number of records in the selection, as an <i>int</i>. Used to determine prompt controls.
    1091       * @return an <i>int</i> specifying the current action. Thus changes in lower parts of the tree continue to effect other disjoint subtrees.
    1092       */
    1093     private int removeMetadata(long id, FileNode record, Metadata data, int action, boolean multiple_selection) {
    1094           ArrayList metadata = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile());
    1095           int user_action = MetaEditPrompt.REMOVE;
    1096           // See if we even have this metadata.
    1097           if(metadata.contains(data)) {
    1098                 ArrayList undo = new ArrayList();
     1000        if(!metadata.contains(data)) {
     1001        // Record undo information for this file node.
     1002        ArrayList undo = new ArrayList();
     1003        // Prepare for MEP
     1004        int user_action = MetaEditPrompt.ACCUMULATE;
     1005        String values = "";
     1006        // See if there is any existing metadata with the same name. If so make a string from all the values (bob, jim etc).
     1007        int metadata_size = metadata.size();
     1008        for(int i = 0; i < metadata_size; i++) {
     1009            Metadata current_data = (Metadata)metadata.get(i);
     1010            if(current_data.getElement().equals(data.getElement())) {
     1011            if(values.length() > 0) {
     1012                values = values + ", ";
     1013            }
     1014            values = values + current_data.getValue();
     1015            }
     1016        }
     1017        // If we are confirming prompt for user_action.
     1018        if(values.length() > 0 && action == MetaEditPrompt.CONFIRM) {
     1019            MetaEditPrompt mep = new MetaEditPrompt(MetaEditPrompt.ADD_PROMPT, multiple_selection, record.getFile(), data.getElement().toString(), values, data.getValue());
     1020            user_action = mep.display();
     1021        }
     1022        if(user_action == MetaEditPrompt.ACCUMULATE_ALL || user_action == MetaEditPrompt.CANCEL || user_action == MetaEditPrompt.OVERWRITE_ALL) {
     1023            action = user_action;
     1024        }
     1025        // If we are overwriting we first remove all metadata with the same element, unless the metadata is non-file level is which case we leave it, and hope the accumulate vs overwrite will be followed during the determining of metadata assigned.
     1026        if(action == MetaEditPrompt.OVERWRITE_ALL || user_action == MetaEditPrompt.OVERWRITE) {
     1027            for(int i = metadata_size; i != 0; i--) {
     1028            Metadata old_data = (Metadata)metadata.get(i - 1);
     1029            if(old_data.getElement().equals(data.getElement()) && old_data.isFileLevel()) {
     1030                // We have a match. Remove this metadata.
     1031                fireMetadataChanged(id, record, old_data, null);
     1032                // Add it to our undo buffer.
     1033                undo.add(old_data);
     1034            }
     1035            }
     1036        }
     1037        // Ensure the metadata will accumulate or overwrite as the user wishes.
     1038        if(user_action == MetaEditPrompt.ACCUMULATE || user_action == MetaEditPrompt.ACCUMULATE_ALL) {
     1039            data.setAccumulate(true);
     1040        }
     1041        else if(user_action == MetaEditPrompt.OVERWRITE || user_action == MetaEditPrompt.OVERWRITE_ALL) {
     1042            data.setAccumulate(false);
     1043        }
     1044        // Unless cancelled, add the metadata after checking we don't already have it in the metadata (obviously not if we're overwriting but we'd better check anyway). Also if we've skipped the file we should do so, but move on to the next child...
     1045        if((user_action == MetaEditPrompt.ACCUMULATE || user_action == MetaEditPrompt.ACCUMULATE_ALL || user_action == MetaEditPrompt.OVERWRITE || user_action == MetaEditPrompt.OVERWRITE_ALL) && !metadata.contains(data)) {
     1046            ///ystem.err.println("Adding metadata " + data);
     1047            fireMetadataChanged(id, record, null, data);
     1048            // The last element in undo is the new element.
     1049            undo.add(data);
     1050        }
     1051        // Store the undo list in our undo buffer.
     1052        undo_buffer.put(record, undo);
     1053        }
     1054    }
     1055    // If we've been cancelled, roll back the addition.
     1056    if(action == MetaEditPrompt.CANCEL) {
     1057        undoAdd(id, record);
     1058    }
     1059    return action;
     1060    }
     1061    /** addMetadata(long, FileNode, Metadata, int, boolean) */
     1062
     1063    /** Create the hidden mds, used for custom classifiers. */
     1064    private MetadataSet createHidden() {
     1065    MetadataSet hidden_mds = new MetadataSet(Utility.METADATA_SET_TEMPLATE);
     1066    hidden_mds.setAttribute("creator","The Gatherer");
     1067    hidden_mds.setAttribute("contact","gatherer@greenstone");
     1068    hidden_mds.setAttribute("description","A hidden metadata set used to create custom classifiers.");
     1069    hidden_mds.setAttribute("family","Gatherer Hidden Metadata");
     1070    hidden_mds.setAttribute("lastchanged","");
     1071    hidden_mds.setAttribute("name","Gatherer Hidden Metadata");
     1072    hidden_mds.setAttribute("namespace",HIDDEN);
     1073    mds_hashtable.put(HIDDEN, hidden_mds);
     1074    fireSetChanged(hidden_mds);
     1075    return hidden_mds;
     1076    }
     1077
     1078    /** Creates a new profiler, which in turn will attempt to load previous profile information. */
     1079    private void loadProfiler() {
     1080    profiler = new MSMProfiler();
     1081    addMSMListener(profiler);
     1082    }
     1083
     1084    /** In order to remove metadata from the tree you first call this method providing it with the metadata you want removed. This will remove any occurance of said metadata from the given FileNode (using fireMetadataChanged()).
     1085     * @param id a unique long identifier common to all actions caused by a single gesture.
     1086     * @param record the FileNode who we are removing metadata from.
     1087     * @param data the <strong>Metadata</strong> you wish removed from the tree.
     1088     * @param action an <i>int</i> specifying the wanted prompting action.
     1089     * @param fire_event <i>true</i> if this action should fire metadata changed events.
     1090     * @param multiple_selection the number of records in the selection, as an <i>int</i>. Used to determine prompt controls.
     1091     * @return an <i>int</i> specifying the current action. Thus changes in lower parts of the tree continue to effect other disjoint subtrees.
     1092     */
     1093    private int removeMetadata(long id, FileNode record, Metadata data, int action, boolean multiple_selection) {
     1094    ArrayList metadata = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile());
     1095    int user_action = MetaEditPrompt.REMOVE;
     1096    // See if we even have this metadata.
     1097    if(metadata.contains(data)) {
     1098        ArrayList undo = new ArrayList();
    10991099                // We do have it. If action == CONFIRM, show user prompt.
    1100                 if(action == MetaEditPrompt.CONFIRM) {
    1101                      MetaEditPrompt mep = new MetaEditPrompt(MetaEditPrompt.REMOVE_PROMPT, multiple_selection, record.getFile(), data.getElement().toString(), data.getValue(), "");
    1102                      user_action = mep.display();
    1103                 }
     1100        if(action == MetaEditPrompt.CONFIRM) {
     1101        MetaEditPrompt mep = new MetaEditPrompt(MetaEditPrompt.REMOVE_PROMPT, multiple_selection, record.getFile(), data.getElement().toString(), data.getValue(), "");
     1102        user_action = mep.display();
     1103        }
    11041104                // Set action to match the user_action under certain circumstances.
    1105                 if(user_action == MetaEditPrompt.CANCEL || user_action == MetaEditPrompt.REMOVE_ALL) {
    1106                      action = user_action;
    1107                 }
    1108                 if(action == MetaEditPrompt.REMOVE_ALL || user_action == MetaEditPrompt.REMOVE) {
    1109                      fireMetadataChanged(id, record, data, null);
    1110                      undo.add(data);
    1111                 }
     1105        if(user_action == MetaEditPrompt.CANCEL || user_action == MetaEditPrompt.REMOVE_ALL) {
     1106        action = user_action;
     1107        }
     1108        if(action == MetaEditPrompt.REMOVE_ALL || user_action == MetaEditPrompt.REMOVE) {
     1109        fireMetadataChanged(id, record, data, null);
     1110        undo.add(data);
     1111        }
    11121112                // Store undo information
    1113                 undo_buffer.put(record, undo);
    1114           }
    1115           // If we've been cancelled higher up, undo action.
    1116           if(action == MetaEditPrompt.CANCEL) {
    1117                 undoRemove(id, record);
    1118           }
    1119           return action;
    1120     }
    1121 
    1122     /** Rollback any changes made as part of a single metadata add process (only valid during the action itself, ie if a user presses cancel).
    1123       * @param id the unique identify of all actions created as part of a single gesture, as a <i>long</i>.
    1124       * @param record the FileNode whose metadata was changed.
    1125       */
    1126     private void undoAdd(long id, FileNode record) {
    1127           // Retrieve the undo data from the buffer
    1128           ArrayList undo = (ArrayList) undo_buffer.get(record);
    1129           // If there is no undo then we can't do anything, but there should be
    1130           if(undo != null && undo.size() > 0) {
     1113        undo_buffer.put(record, undo);
     1114    }
     1115    // If we've been cancelled higher up, undo action.
     1116    if(action == MetaEditPrompt.CANCEL) {
     1117        undoRemove(id, record);
     1118    }
     1119    return action;
     1120    }
     1121
     1122    /** Rollback any changes made as part of a single metadata add process (only valid during the action itself, ie if a user presses cancel).
     1123     * @param id the unique identify of all actions created as part of a single gesture, as a <i>long</i>.
     1124     * @param record the FileNode whose metadata was changed.
     1125     */
     1126    private void undoAdd(long id, FileNode record) {
     1127    // Retrieve the undo data from the buffer
     1128    ArrayList undo = (ArrayList) undo_buffer.get(record);
     1129    // If there is no undo then we can't do anything, but there should be
     1130    if(undo != null && undo.size() > 0) {
    11311131                // The last piece of data in an add actions undo buffer is the metadata that was added
    1132                 Metadata data = (Metadata) undo.remove(undo.size() - 1);
     1132        Metadata data = (Metadata) undo.remove(undo.size() - 1);
    11331133                // Remove the data
    1134                 fireMetadataChanged(id, record, data, null);
     1134        fireMetadataChanged(id, record, data, null);
    11351135                // If we removed other metadata when adding this metadata restore it too
    1136                 for(int i = 0; i < undo.size(); i++) {
    1137                      Metadata old_data = (Metadata) undo.get(i);
    1138                      fireMetadataChanged(id, record, null, old_data);
    1139                 }
    1140           }
    1141     }
    1142     /** Rollback any changes made as part of a single metadata remove process (only valid during the action itself, ie if a user presses cancel).
    1143       * @param id the unique identify of all actions created as part of a single gesture, as a long.
    1144       * @param record the FileNode metadata was removed from.
    1145       */
    1146     private void undoRemove(long id, FileNode record) {
    1147           // Retrieve undo information
    1148           ArrayList undo = (ArrayList) undo_buffer.get(record);
    1149           // Ensure that we have something to undo
    1150           if(undo != null && undo.size() == 1) {
     1136        for(int i = 0; i < undo.size(); i++) {
     1137        Metadata old_data = (Metadata) undo.get(i);
     1138        fireMetadataChanged(id, record, null, old_data);
     1139        }
     1140    }
     1141    }
     1142    /** Rollback any changes made as part of a single metadata remove process (only valid during the action itself, ie if a user presses cancel).
     1143     * @param id the unique identify of all actions created as part of a single gesture, as a long.
     1144     * @param record the FileNode metadata was removed from.
     1145     */
     1146    private void undoRemove(long id, FileNode record) {
     1147    // Retrieve undo information
     1148    ArrayList undo = (ArrayList) undo_buffer.get(record);
     1149    // Ensure that we have something to undo
     1150    if(undo != null && undo.size() == 1) {
    11511151                // The undo buffer should contain exactly one entry, the metadata removed
    1152                 Metadata data = (Metadata) undo.get(0);
    1153                 fireMetadataChanged(id, record, null, data);
    1154           }
    1155     }
    1156     /** Roll back any changes made as part of a single metadata update process (only valid during the action itself, ie if a user presses cancel).
    1157       * @param id the unique identify of all actions created as part of a single gesture, as a long.
    1158       * @param record the FileNode whose metadata was changed.
    1159       */
    1160     private void undoUpdate(long id, FileNode record) {
    1161           // Retrieve undo information
    1162           ArrayList undo = (ArrayList) undo_buffer.get(record);
    1163           if(undo != null && undo.size() == 2) {
    1164                 Metadata old_data = (Metadata) undo.get(0);
    1165                 Metadata new_data = (Metadata) undo.get(1);
    1166                 fireMetadataChanged(id, record, new_data, null);
    1167                 if(old_data != new_data) { // Correct reference comparison
    1168                      fireMetadataChanged(id, record, null, old_data);
    1169                 }
    1170           }
    1171     }
    1172 
    1173     /** Used to update the values of one of the metadata elements within this node. Has the same trickiness as Add but only half the number of options.
    1174       * @param id the unique identify of all actions created as part of a single gesture, as a <i>long</i>.
    1175       * @param record the FileNode whose metadata we are changing.
    1176       * @param old_data The old existing <strong>Metadata</strong>.
    1177       * @param new_data The new updated <strong>Metadata</strong>.
    1178       * @param action An <i>int</i> indicating what we are going to do about it.
    1179       * @param multiple_selection The number of records in the selection, as an <i>int</i>. Used to determine prompt controls.
    1180       * @return An <i>int</i> specifying the current action. Thus changes in lower parts of the tree continue to effect other disjoint subtrees.
    1181       */
    1182     private int updateMetadata(long id, FileNode record, Metadata old_data, Metadata new_data, int action, boolean multiple_selection, boolean file_level) {
    1183           ArrayList metadata;
    1184           if(file_level) {
    1185                 metadata = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile());
    1186           }
    1187           else {
    1188                 metadata = Gatherer.c_man.getCollection().gdm.getAllMetadata(record.getFile());
    1189           }
    1190           int user_action = MetaEditPrompt.OVERWRITE;
    1191           // Standard case of updating an existing metadata value.
    1192           if(metadata.contains(old_data)) {
    1193                 ArrayList undo = new ArrayList();
     1152        Metadata data = (Metadata) undo.get(0);
     1153        fireMetadataChanged(id, record, null, data);
     1154    }
     1155    }
     1156    /** Roll back any changes made as part of a single metadata update process (only valid during the action itself, ie if a user presses cancel).
     1157     * @param id the unique identify of all actions created as part of a single gesture, as a long.
     1158     * @param record the FileNode whose metadata was changed.
     1159     */
     1160    private void undoUpdate(long id, FileNode record) {
     1161    // Retrieve undo information
     1162    ArrayList undo = (ArrayList) undo_buffer.get(record);
     1163    if(undo != null && undo.size() == 2) {
     1164        Metadata old_data = (Metadata) undo.get(0);
     1165        Metadata new_data = (Metadata) undo.get(1);
     1166        fireMetadataChanged(id, record, new_data, null);
     1167        if(old_data != new_data) { // Correct reference comparison
     1168        fireMetadataChanged(id, record, null, old_data);
     1169        }
     1170    }
     1171    }
     1172
     1173    /** Used to update the values of one of the metadata elements within this node. Has the same trickiness as Add but only half the number of options.
     1174     * @param id the unique identify of all actions created as part of a single gesture, as a <i>long</i>.
     1175     * @param record the FileNode whose metadata we are changing.
     1176     * @param old_data The old existing <strong>Metadata</strong>.
     1177     * @param new_data The new updated <strong>Metadata</strong>.
     1178     * @param action An <i>int</i> indicating what we are going to do about it.
     1179     * @param multiple_selection The number of records in the selection, as an <i>int</i>. Used to determine prompt controls.
     1180     * @return An <i>int</i> specifying the current action. Thus changes in lower parts of the tree continue to effect other disjoint subtrees.
     1181     */
     1182    private int updateMetadata(long id, FileNode record, Metadata old_data, Metadata new_data, int action, boolean multiple_selection, boolean file_level) {
     1183    ArrayList metadata;
     1184    if(file_level) {
     1185        metadata = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile());
     1186    }
     1187    else {
     1188        metadata = Gatherer.c_man.getCollection().gdm.getAllMetadata(record.getFile());
     1189    }
     1190    int user_action = MetaEditPrompt.OVERWRITE;
     1191    // Standard case of updating an existing metadata value.
     1192    if(metadata.contains(old_data)) {
     1193        ArrayList undo = new ArrayList();
    11941194                // If we are to prompt the user, do so.
    1195                 if(action == MetaEditPrompt.CONFIRM) {
    1196                      MetaEditPrompt mep = new MetaEditPrompt(MetaEditPrompt.UPDATE_PROMPT, multiple_selection, record.getFile(), old_data.getElement().toString(), old_data.getValue(), new_data.getValue());
    1197                      user_action = mep.display();
    1198                 }
     1195        if(action == MetaEditPrompt.CONFIRM) {
     1196        MetaEditPrompt mep = new MetaEditPrompt(MetaEditPrompt.UPDATE_PROMPT, multiple_selection, record.getFile(), old_data.getElement().toString(), old_data.getValue(), new_data.getValue());
     1197        user_action = mep.display();
     1198        }
    11991199                // Some user actions should have a continuous effect.
    1200                 if(user_action == MetaEditPrompt.OVERWRITE_ALL || user_action == MetaEditPrompt.CANCEL) {
    1201                      action = user_action;
    1202                 }
     1200        if(user_action == MetaEditPrompt.OVERWRITE_ALL || user_action == MetaEditPrompt.CANCEL) {
     1201        action = user_action;
     1202        }
    12031203                // And if the update chose update, do so.
    1204                 if(action == MetaEditPrompt.OVERWRITE_ALL || user_action == MetaEditPrompt.OVERWRITE || user_action == MetaEditPrompt.UPDATE_ONCE) {
    1205                      ///ystem.err.println("Updating:\n"+old_data+"\nto\n"+new_data);
    1206                      // If this is file level then we can do a normal replace
    1207                      if(file_level) {
    1208                           fireMetadataChanged(id, record, old_data, new_data);
    1209                           undo.add(old_data);
    1210                           undo.add(new_data);
    1211                      }
    1212                      // Otherwise we are dealing with someone attempting to override inherited metadata, so we actually fire an add. To this end we add new data twice to the undo buffer, thus we can detect if this has happened.
    1213                      else {
    1214                           fireMetadataChanged(id, record, null, new_data);
    1215                           undo.add(new_data);
    1216                           undo.add(new_data);
    1217                      }
    1218                 }
     1204        if(action == MetaEditPrompt.OVERWRITE_ALL || user_action == MetaEditPrompt.OVERWRITE || user_action == MetaEditPrompt.UPDATE_ONCE) {
     1205        ///ystem.err.println("Updating:\n"+old_data+"\nto\n"+new_data);
     1206        // If this is file level then we can do a normal replace
     1207        if(file_level) {
     1208            fireMetadataChanged(id, record, old_data, new_data);
     1209            undo.add(old_data);
     1210            undo.add(new_data);
     1211        }
     1212        // Otherwise we are dealing with someone attempting to override inherited metadata, so we actually fire an add. To this end we add new data twice to the undo buffer, thus we can detect if this has happened.
     1213        else {
     1214            fireMetadataChanged(id, record, null, new_data);
     1215            undo.add(new_data);
     1216            undo.add(new_data);
     1217        }
     1218        }
    12191219                // Store the undo information
    1220                 undo_buffer.put(record, undo);
    1221           }
    1222           // If we've been cancelled undo.
    1223           if(action == MetaEditPrompt.CANCEL) {
    1224                 undoUpdate(id, record);
    1225           }
    1226           return action;
    1227     }
     1220        undo_buffer.put(record, undo);
     1221    }
     1222    // If we've been cancelled undo.
     1223    if(action == MetaEditPrompt.CANCEL) {
     1224        undoUpdate(id, record);
     1225    }
     1226    return action;
     1227    }
    12281228}
  • trunk/gli/src/org/greenstone/gatherer/msm/parsers/GreenstoneMetadataParser.java

    r4316 r4365  
    6262 */
    6363public class GreenstoneMetadataParser
    64     extends LinkedHashMap
    65     implements MetadataParser {
    66 
    67     static final private int MAX_CFG_CACHE_SIZE = 10;
    68     static final private int MAX_GDM_CACHE_SIZE = 10;
    69     /** The default name and location for a collection configuration file (presuming that a collection file prefix will be added). */
    70     static final private String CONFIG_FILENAME = "etc" + File.separator + "collect.cfg";
    71     /** The pattern to match when searching for directory level assignments. */
    72     static final private String DIRECTORY_FILENAME = ".*";
    73     static final private String DIRECTORY_FILENAME_SUFFIX = "/.*";
    74     static final private String DESCRIPTION_ELEMENT = "Description";
    75     static final private String FILENAME_ELEMENT = "FileName";
    76     static final private String FILESET_ELEMENT = "FileSet";
    77     /** The name of a gdm file. */
    78     static final private String GIMPORT = "gimport";
    79     static final private String IMPORT = "import";
    80     static final private String METADATA_ELEMENT = "Metadata";
    81     static final private String METADATA_XML_FILENAME = "metadata.xml";
    82     static final private String MODE_ATTRIBUTE = "mode";
    83     static final private String NAME_ATTRIBUTE = "name";
    84     static final private String SEPARATOR = "/";
    85 
    86     /** A list of the collect.cfg paths that we should ignore. */
    87     private ArrayList ignore_list = new ArrayList();
    88     /** Has this process been cancelled. */
    89     private boolean dialog_cancelled = false;
    90     /** A cache of previously parsed collection configuration files. */
    91     private CollectCFGCache cfg_cache = new CollectCFGCache();
    92     /** A mapping from BasicMetadata to their fully enabled Metadata incarnation. */
    93     private HashMap transform = new HashMap();
    94 
    95     /** Default constructor needed for dynamic class loading. */
    96     public GreenstoneMetadataParser() {
    97     }
    98     /** Locate and import any metadata parsed by this metadata parser given the file involved and its previous incarnation. */
    99     public boolean process(FileNode destination, FileNode origin, boolean folder_level, boolean dummy_run) {
    100           ///atherer.println("GreenstoneMetadataParser: Process " + origin + ": ");
    101           int counter = 0;
    102           dialog_cancelled = false;
    103 
    104           // 1. Determine what collection the file is in, and load/parse the appropriate collect.cfg. Cache collect.cfg object.
    105           ///ystem.err.print("1 ");
    106           // Start at the origin node file. If its a file get its parent directory.
    107           File collection_dir = origin.getFile();
    108           if(collection_dir.isFile()) {
    109                 collection_dir = collection_dir.getParentFile();
    110           }
    111           // We're currently in the importing directory so we'll go one more step up.
    112           collection_dir = collection_dir.getParentFile();
    113           // We are looking for a directory which contains a etc/collect.cfg file and either an import or a gimport directory.
    114           boolean found = false;
    115           while(!found && collection_dir != null) {
    116                 File possible_cfg_file = new File(collection_dir, CONFIG_FILENAME);
    117                 File possible_gimport_directory = new File(collection_dir, GIMPORT);
    118                 File possible_import_directory = new File(collection_dir, IMPORT);
    119                 if(possible_cfg_file.exists() && (possible_gimport_directory.exists() || possible_import_directory.exists())) {
    120                      found = true;
    121                      ///ystem.err.println("Found greenstone collection at " + collection_dir.getAbsolutePath());
    122                 }
    123                 else {
    124                      collection_dir = collection_dir.getParentFile();
    125                 }
    126           }
    127 
    128           // Now retrieve the configuration file if there is one.
    129           CollectCFG collect_cfg = null;
    130           if(collection_dir != null) {
    131                 File collect_cfg_file = new File(collection_dir, CONFIG_FILENAME);
    132                 if(collect_cfg_file.exists()) {
    133                      collect_cfg = cfg_cache.get(collect_cfg_file);
    134                 }
    135           }
    136 
    137           // Continue only if we are sure this is a greenstone collection
    138           if(collection_dir != null && collect_cfg != null) {
     64    extends LinkedHashMap
     65    implements MetadataParser {
     66
     67    static final private int MAX_CFG_CACHE_SIZE = 10;
     68    static final private int MAX_GDM_CACHE_SIZE = 10;
     69    /** The default name and location for a collection configuration file (presuming that a collection file prefix will be added). */
     70    static final private String CONFIG_FILENAME = "etc" + File.separator + "collect.cfg";
     71    /** The pattern to match when searching for directory level assignments. */
     72    static final private String DIRECTORY_FILENAME = ".*";
     73    static final private String DIRECTORY_FILENAME_SUFFIX = "/.*";
     74    static final private String DESCRIPTION_ELEMENT = "Description";
     75    static final private String FILENAME_ELEMENT = "FileName";
     76    static final private String FILESET_ELEMENT = "FileSet";
     77    /** The name of a gdm file. */
     78    static final private String GIMPORT = "gimport";
     79    static final private String IMPORT = "import";
     80    static final private String METADATA_ELEMENT = "Metadata";
     81    static final private String METADATA_XML_FILENAME = "metadata.xml";
     82    static final private String MODE_ATTRIBUTE = "mode";
     83    static final private String NAME_ATTRIBUTE = "name";
     84    static final private String SEPARATOR = "/";
     85
     86    /** A list of the collect.cfg paths that we should ignore. */
     87    private ArrayList ignore_list = new ArrayList();
     88    /** Has this process been cancelled. */
     89    private boolean dialog_cancelled = false;
     90    /** A cache of previously parsed collection configuration files. */
     91    private CollectCFGCache cfg_cache = new CollectCFGCache();
     92    /** A mapping from BasicMetadata to their fully enabled Metadata incarnation. */
     93    private HashMap transform = new HashMap();
     94
     95    /** Default constructor needed for dynamic class loading. */
     96    public GreenstoneMetadataParser() {
     97    }
     98    /** Locate and import any metadata parsed by this metadata parser given the file involved and its previous incarnation. */
     99    public boolean process(FileNode destination, FileNode origin, boolean folder_level, boolean dummy_run) {
     100    ///atherer.println("GreenstoneMetadataParser: Process " + origin + ": ");
     101    int counter = 0;
     102    dialog_cancelled = false;
     103
     104    // 1. Determine what collection the file is in, and load/parse the appropriate collect.cfg. Cache collect.cfg object.
     105    ///ystem.err.print("1 ");
     106    // Start at the origin node file. If its a file get its parent directory.
     107    File collection_dir = origin.getFile();
     108    if(collection_dir.isFile()) {
     109        collection_dir = collection_dir.getParentFile();
     110    }
     111    // We're currently in the importing directory so we'll go one more step up.
     112    collection_dir = collection_dir.getParentFile();
     113    // We are looking for a directory which contains a etc/collect.cfg file and either an import or a gimport directory.
     114    boolean found = false;
     115    while(!found && collection_dir != null) {
     116        File possible_cfg_file = new File(collection_dir, CONFIG_FILENAME);
     117        File possible_gimport_directory = new File(collection_dir, GIMPORT);
     118        File possible_import_directory = new File(collection_dir, IMPORT);
     119        if(possible_cfg_file.exists() && (possible_gimport_directory.exists() || possible_import_directory.exists())) {
     120        found = true;
     121        ///ystem.err.println("Found greenstone collection at " + collection_dir.getAbsolutePath());
     122        }
     123        else {
     124        collection_dir = collection_dir.getParentFile();
     125        }
     126    }
     127
     128    // Now retrieve the configuration file if there is one.
     129    CollectCFG collect_cfg = null;
     130    if(collection_dir != null) {
     131        File collect_cfg_file = new File(collection_dir, CONFIG_FILENAME);
     132        if(collect_cfg_file.exists()) {
     133        collect_cfg = cfg_cache.get(collect_cfg_file);
     134        }
     135    }
     136
     137    // Continue only if we are sure this is a greenstone collection
     138    if(collection_dir != null && collect_cfg != null) {
    139139                // 2. Attempt to merge in any mdses and make note of those that are successfully imported (by removing reference from collect.cfg).
    140140                ///ystem.err.print("2 ");
    141                 ArrayList mdses = collect_cfg.getMetadataSets();
    142                 for(int i = 0; i < mdses.size(); i++) {
    143                      File mds_file = (File) mdses.get(i);
    144                      Gatherer.c_man.getCollection().msm.importMDS(mds_file, false);
    145                 }
    146                 mdses.clear();
    147                 mdses = null;
     141        ArrayList mdses = collect_cfg.getMetadataSets();
     142        for(int i = 0; i < mdses.size(); i++) {
     143        File mds_file = (File) mdses.get(i);
     144        Gatherer.c_man.getCollection().msm.importMDS(mds_file, false);
     145        }
     146        mdses.clear();
     147        mdses = null;
    148148
    149149                // 3. Locate all of the metadata.xml files that may have an affect on the origin file. Make sure the metadata.xml closest to the origin files directory is last (to ensure property inheritance regarding accumulate/overwrite).
    150150                ///ystem.err.print("3 ");
    151                 ArrayList search_files = new ArrayList();
    152                 File file = origin.getFile();
    153                 String filename = null;
    154                 if(file.isFile()) {
    155                      filename = file.getName();
    156                      file = file.getParentFile();
    157                 }
    158                 while(!file.equals(collection_dir)) {
    159                      File test_file = new File(file, Utility.METADATA_XML);
    160                      if(test_file.exists()) {
    161                           search_files.add(0, new MetadataXMLFileSearch(test_file, filename));
    162                      }
    163                      if(filename != null) {
    164                           filename = file.getName() + SEPARATOR + filename;
    165                      }
    166                      else {
    167                           filename = file.getName();
    168                      }
    169                      file = file.getParentFile();
    170                 }
    171                 filename = null;
    172                 file = null;
     151        ArrayList search_files = new ArrayList();
     152        File file = origin.getFile();
     153        String filename = null;
     154        if(file.isFile()) {
     155        filename = file.getName();
     156        file = file.getParentFile();
     157        }
     158        while(!file.equals(collection_dir)) {
     159        File test_file = new File(file, Utility.METADATA_XML);
     160        if(test_file.exists()) {
     161            search_files.add(0, new MetadataXMLFileSearch(test_file, filename));
     162        }
     163        if(filename != null) {
     164            filename = file.getName() + SEPARATOR + filename;
     165        }
     166        else {
     167            filename = file.getName();
     168        }
     169        file = file.getParentFile();
     170        }
     171        filename = null;
     172        file = null;
    173173                // Start with an initially empty ArrayList of metadata
    174                 ArrayList metadatum = new ArrayList();
     174        ArrayList metadatum = new ArrayList();
    175175                // Now search each of these metadata xml for metadata, remembering to accumulate or overwrite as we go along.
    176                 for(int i = 0; i < search_files.size(); i++) {
    177                      MetadataXMLFileSearch a_search = (MetadataXMLFileSearch) search_files.get(i);
    178                      ///ystem.err.println("Search " + a_search.file.getAbsolutePath() + " for " + (a_search.filename != null ? a_search.filename : ".*"));
    179                      // Retrieve the document
    180                      BasicGDMDocument document = getDocument(a_search.file);
    181                      if(document != null) {
    182                           // If this is a dummy run, our original source file is actually the metadata.xml file and we retrieve all metadata for this collection, as if accumulated!
    183                           if(dummy_run) {
    184                                 metadatum = document.getAllMetadata();
    185                           }
    186                           else {
    187                                 metadatum = document.getMetadata(a_search.filename, metadatum, true);
    188                           }
    189                           document = null;
    190                      }
    191                      a_search = null;
    192                 }
    193                 search_files = null;
     176        for(int i = 0; i < search_files.size(); i++) {
     177        MetadataXMLFileSearch a_search = (MetadataXMLFileSearch) search_files.get(i);
     178        ///ystem.err.println("Search " + a_search.file.getAbsolutePath() + " for " + (a_search.filename != null ? a_search.filename : ".*"));
     179        // Retrieve the document
     180        BasicGDMDocument document = getDocument(a_search.file);
     181        if(document != null) {
     182            // If this is a dummy run, our original source file is actually the metadata.xml file and we retrieve all metadata for this collection, as if accumulated!
     183            if(dummy_run) {
     184            metadatum = document.getAllMetadata();
     185            }
     186            else {
     187            metadatum = document.getMetadata(a_search.filename, metadatum, true);
     188            }
     189            document = null;
     190        }
     191        a_search = null;
     192        }
     193        search_files = null;
    194194                // Finally assign the metadata
    195195                ///ystem.err.println("Found " + metadatum.size() + " pieces of metadata for " + destination);
    196                 if(metadatum.size() > 0) {
    197                      addMetadata(destination, metadatum, collection_dir, collect_cfg, dummy_run);
     196        if(metadatum.size() > 0) {
     197        addMetadata(destination, metadatum, collection_dir, collect_cfg, dummy_run);
     198        }
     199    }
     200    else {
     201                ///ystem.err.println("Not a greenstone collection (no collect.cfg found).");
     202    }
     203    return dialog_cancelled;
     204    }
     205
     206    protected boolean removeEldestEntry(java.util.Map.Entry entry) {
     207    return (size() > MAX_GDM_CACHE_SIZE);
     208    }
     209
     210    private void addMetadata(FileNode destination, ArrayList metadatum, File collection_dir, CollectCFG collect_cfg, boolean dummy_run) {
     211    ///ystem.err.print("6 ");
     212    // Used in a complicated test later on.
     213    for(int i = 0; !dialog_cancelled && i < metadatum.size(); i++) {
     214        BasicMetadata basic_metadata = (BasicMetadata) metadatum.get(i);
     215        BasicMetadata metadata = (BasicMetadata) metadatum.get(i);
     216        metadata.collection = collection_dir;
     217        Metadata final_metadata = null;
     218                // If this BasicMetadata already exists in the transform cache then we can save ourselves a lot of work.
     219        SoftReference reference = (SoftReference) transform.get(basic_metadata);
     220        if(reference != null) {
     221        final_metadata = (Metadata) reference.get();
     222        }
     223        if(final_metadata == null) {
     224        ///ystem.err.println("No existing Metadata object for BasicMetadata: " + basic_metadata);
     225        // 6a. Check if an hfile is associated with this metadata, and if so load it, cache it in the collection.cfg object, then resolve metadata value index.
     226        HFile h_file = collect_cfg.getHFile(metadata.element);
     227        if(h_file != null && !dummy_run) {
     228            ///ystem.err.print(metadata.value + " maps to ");
     229            metadata.value = h_file.getValue(metadata.value);
     230            ///ystem.err.println(metadata.value);
     231        }
     232        h_file = null;
     233        // 6b. Check if there is a profile regarding the current metadata.
     234        ///ystem.err.println("Retrieve existing action: " + collection_dir.getAbsolutePath() + ", " + metadata.element);
     235        if(Gatherer.c_man.getCollection().msm.profiler.containsAction(collection_dir.getAbsolutePath(), metadata.element)) {
     236            String new_element_name = Gatherer.c_man.getCollection().msm.profiler.getAction(collection_dir.getAbsolutePath(), metadata.element);
     237            ///ystem.err.println("Profile result = " + new_element_name);
     238            if(new_element_name == null) {
     239            metadata = null;
     240            }
     241            else {
     242            metadata.element = new_element_name;
     243            }
     244            new_element_name = null;
     245        }
     246        ///atherer.println("Assigning metadata.");
     247        if(metadata != null) {
     248            // 6c. Try to add metadata. If there is no matching metadata element:
     249            ElementWrapper element = Gatherer.c_man.getCollection().msm.getElement(metadata.element);
     250            // Arg. The element returned may come from the Greenstone dls, which of course should never be involved during importing. To solve check the namespace isn't "" and if it is nullify the element. Nullify. NULLIFY, Bwuhahahaha...
     251            if(element != null && element.getNamespace().equals("")) {
     252            element = null;
     253            }
     254            // 6ci. If no match exists, prompt the user to add/merge with specific metadata element. The user can also choose to ignore this metadata.
     255            if(element == null) {
     256            element = selectElement(metadata.element);
     257            if(!dialog_cancelled) {
     258                // 6ciii. If either of the above work, remember to add to profile.
     259                if(element == null) {
     260                ///ystem.err.println("Adding profile action: " + collection_dir.getAbsolutePath() + ", " + metadata.element + ", null");
     261                Gatherer.c_man.getCollection().msm.profiler.addAction(collection_dir.getAbsolutePath(), metadata.element, null);
     262                }
     263                else {
     264                ///ystem.err.println("Adding profile action: " + collection_dir.getAbsolutePath() + ", " + metadata.element + ", " + element.getName());
     265                Gatherer.c_man.getCollection().msm.profiler.addAction(collection_dir.getAbsolutePath(), metadata.element, element.getName());
     266                }
     267            }
     268            }
     269            // - Add metadata
     270            if(!dummy_run && element != null && !dialog_cancelled) {
     271            ///ystem.err.println("Retrieve the value tree for " + element.toString());
     272            GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(element);
     273            if(model != null) {
     274                GValueNode node = model.addValue(metadata.value);
     275                final_metadata = new Metadata(element, node);
     276                ///ystem.err.println("Adding final metadata: " + metadata.toString());
     277                node = null;
     278            }
     279            model = null;
     280            }
     281            element = null;
     282        }
     283        // If we have successfully created a Metadata from the BasicMetadata, store it
     284        if(final_metadata != null && !dialog_cancelled) {
     285            transform.put(basic_metadata, new SoftReference(final_metadata));
     286            ///ystem.err.println("Add a Metadata object for BasicMetadata: " + basic_metadata);
     287        }
     288        }
     289        else {
     290        ///ystem.err.println("Found a Metadata object for BasicMetadata: " + basic_metadata);
     291        }
     292        if(!dummy_run && final_metadata != null && !dialog_cancelled) {
     293        final_metadata.setAccumulate(metadata.accumulates);
     294        // Now we can finally add the metadata.
     295        ///ystem.err.println("Adding Metadata: " + final_metadata);
     296        Gatherer.c_man.getCollection().msm.fireMetadataChanged(0, destination, null, final_metadata);
     297        }
     298                // Otherwise there is no way to add this metadata. No value model no metadata value.
     299        final_metadata = null;
     300        metadata = null;
     301    }
     302    }
     303
     304    /** Determine the different suffix between two string.
     305     * @param base_str The base <strong>String</strong>, expected to be the short of the two strings provided.
     306     * @param target_str The target <strong>String</strong>, whose differing suffix is returned.
     307     * @return A <strong>String</strong> containing the suffix from target which is different from base.
     308     */
     309    private String diff(String base_str, String target_str) {
     310    StringTokenizer base_tokenizer = new StringTokenizer(base_str, File.separator);
     311    StringTokenizer target_tokenizer = new StringTokenizer(target_str, File.separator);
     312    String base = null;
     313    String target = null;
     314    while(base_tokenizer.hasMoreTokens() && (base = base_tokenizer.nextToken()).equals((target = target_tokenizer.nextToken()))) {
     315    }
     316    StringBuffer result = new StringBuffer(target);
     317    while(target_tokenizer.hasMoreTokens()) {
     318        result.append(File.separator);
     319        result.append(target_tokenizer.nextToken());
     320    }
     321    return result.toString();
     322    }
     323
     324    /** Retrieve the BasicGDMDocument found at the given file, or null if there is no such file or if it isn't a valid BasicGDMDocument. */
     325    private BasicGDMDocument getDocument(File file) {
     326    ///ystem.err.println("Get Document at: " + file.getAbsolutePath());
     327    BasicGDMDocument document = null;
     328    if(!ignore_list.contains(file) && file.exists()) {
     329                // Check cache
     330        SoftReference reference = (SoftReference) get(file);
     331        if(reference != null) {
     332        ///ystem.err.println("Hit!!");
     333        document = (BasicGDMDocument) reference.get();
     334        reference = null;
     335        }
     336                // If that didn't work try to parse in the document
     337        if(document == null) {
     338        ///ystem.err.println("Miss or stale reference.");
     339        document = new BasicGDMDocument(file);
     340        if(document.isValid()) {
     341            put(file, new SoftReference(document));
     342        }
     343        else {
     344            ///ystem.err.println(file.getAbsolutePath() + " is not a valid GDM XML file.");
     345            ignore_list.add(file);
     346            document = null;
     347        }
     348        }
     349    }
     350    else {
     351                ///ystem.err.println("Ignoring file or file doesn't exists.");
     352    }
     353    return document;
     354    }
     355
     356
     357    /** Display a prompt allowing a user to select a metadata element to attempt to force add/merge or ignore a metadata element to. For instance an old version of a metadata.xml from the DLS collection might have an assigned metadata value "Publisher=EC Courier", however Publisher won't automatically match to any metadata set. This prompt will be displayed, and some effort will be made to systematically locate the appropriate set. In this case this should be the DLS metadata set as dls.Publisher should be the closest match. Regardless the element selected is returned.
     358     * @param element_name The name of the element we are trying to add, as a <strong>String</strong>.
     359     * @return The <strong>ElementWrapper</strong> choosen by the user, or <i>null</i> to skip this metadata element.
     360     */
     361    private ElementWrapper selectElement(String element_name) {
     362    ElementWrapper result = Gatherer.c_man.getCollection().msm.prompt.selectElement(element_name);
     363    dialog_cancelled = Gatherer.c_man.getCollection().msm.prompt.wasDialogCancelled();
     364    return result;
     365    }
     366
     367    /** A 'basic' version of the more complete GDMDocument used elsewhere, this object provides the same functionality except that it doesn't use Metadata objects. These objects require live references to elements within the MetadataSetManager and GValueModels, but these may not yet exist (and indeed may never exist) for metadata parsed from metadata.xml's outside of our current collection. Thus this class returns a String (or an ArrayList of Strings) when asked for the metadata associated with a certain file. Also notice that this class provides no constructor method for creating a blank document, nor does it ever need a reference to the Gatherer.*/
     368    private class BasicGDMDocument
     369    extends HashMap {
     370    /** The document this class sources its data from. */
     371    private Document base_document;
     372    /** This constructor takes the original document and parsed out and stores metadata with its association to filenames. */
     373    public BasicGDMDocument(File file) {
     374                ///ystem.err.println("New BasicGDMDocument: " + file.getAbsolutePath());
     375        base_document = Utility.parse(file.getAbsolutePath(), false);
     376    }
     377    /** Retrieve all of the metadata in this file. */
     378    public ArrayList getAllMetadata() {
     379        ArrayList metadatum = new ArrayList();
     380                // Don't search the cache as this would never have been added.
     381        try {
     382        // Retrieve the document element.
     383        Element directorymetadata_element = base_document.getDocumentElement();
     384        // Iterate through the filesets, checking the FileName child element against    the target file's name using regular expression matching.
     385        NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT);
     386        for(int i = 0; i < fileset_elements.getLength(); i++) {
     387            Element fileset_element = (Element) fileset_elements.item(i);
     388            NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT);
     389            for(int j = 0; j < filename_elements.getLength(); j++) {
     390            Element filename_element = (Element) filename_elements.item(j);
     391            // If they match add all of the metadata found in the Description child element, overwriting any metadata with the same element
     392            NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT);
     393            for(int k = 0; k < description_elements.getLength(); k++) {
     394                Element description_element = (Element) description_elements.item(k);
     395                NodeList metadata_elements = description_element.getElementsByTagName(METADATA_ELEMENT);
     396                for(int l = 0; l < metadata_elements.getLength(); l++) {
     397                Element metadata_element = (Element) metadata_elements.item(l);
     398                String element = metadata_element.getAttribute(NAME_ATTRIBUTE);
     399                BasicMetadata metadata = new BasicMetadata(element, Utility.METADATA_XML, true);
     400                // Remove any previous values for this metadata element.
     401                for(int m = metadatum.size() - 1; m >= 0; m--) {
     402                    BasicMetadata old_metadata = (BasicMetadata) metadatum.get(m);
     403                    if(old_metadata.element.equals(element)) {
     404                    metadatum.remove(m);
     405                    }
     406                    old_metadata = null;
    198407                }
    199           }
    200           else {
    201                 ///ystem.err.println("Not a greenstone collection (no collect.cfg found).");
    202           }
    203           return dialog_cancelled;
    204      }
    205 
    206      protected boolean removeEldestEntry(java.util.Map.Entry entry) {
    207           return (size() > MAX_GDM_CACHE_SIZE);
    208      }
    209 
    210      private void addMetadata(FileNode destination, ArrayList metadatum, File collection_dir, CollectCFG collect_cfg, boolean dummy_run) {
    211           ///ystem.err.print("6 ");
    212           // Used in a complicated test later on.
    213           for(int i = 0; !dialog_cancelled && i < metadatum.size(); i++) {
    214                 BasicMetadata basic_metadata = (BasicMetadata) metadatum.get(i);
    215                 BasicMetadata metadata = (BasicMetadata) metadatum.get(i);
    216                 metadata.collection = collection_dir;
    217                 Metadata final_metadata = null;
    218                 // If this BasicMetadata already exists in the transform cache then we can save ourselves a lot of work.
    219                 SoftReference reference = (SoftReference) transform.get(basic_metadata);
    220                 if(reference != null) {
    221                      final_metadata = (Metadata) reference.get();
     408                // Add the completed metadata and clean up
     409                metadatum.add(metadata);
     410                metadata = null;
     411                element = null;
     412                metadata_element = null;
     413                }
     414                metadata_elements = null;
     415                description_element = null;
     416            }
     417            description_elements = null;
     418            filename_element = null;
     419            }
     420            filename_elements = null;
     421            fileset_element = null;
     422        }
     423        fileset_elements = null;
     424        directorymetadata_element = null;
     425        }
     426        catch (Exception error) {
     427        Gatherer.self.printStackTrace(error);
     428        }
     429        return metadatum;
     430    }
     431         
     432    /** Retrieve any metadata associated with a certain file. If filename is null we are attempting to find directory level metadata. */
     433    public ArrayList getMetadata(String filename, ArrayList metadatum_so_far, boolean folder_level) {
     434                ///ystem.err.println("Retrieving metadata for: " + filename);
     435        ArrayList metadatum = null;
     436                // We start by attempting to retrieve this metadata from the cache.
     437        if(filename != null) {
     438        metadatum = (ArrayList) get(filename);
     439        }
     440        else {
     441        metadatum = (ArrayList) get(DIRECTORY_FILENAME);
     442        }
     443                // If that failed we consult the document for metadata.
     444        if(metadatum == null) {
     445        metadatum = new ArrayList();
     446        if(metadatum_so_far == null) {
     447            metadatum = new ArrayList();
     448        }
     449        else {
     450            metadatum = metadatum_so_far;
     451        }
     452        try {
     453            // Retrieve the document element.
     454            Element directorymetadata_element = base_document.getDocumentElement();
     455            // Iterate through the filesets, checking the FileName child element against    the target file's name using regular expression matching.
     456            NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT);
     457            for(int i = 0; i < fileset_elements.getLength(); i++) {
     458            Element fileset_element = (Element) fileset_elements.item(i);
     459            NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT);
     460            for(int j = 0; j < filename_elements.getLength(); j++) {
     461                Element filename_element = (Element) filename_elements.item(j);
     462                String filename_text = MSMUtils.getValue(filename_element);
     463                // Crappy. There are apparently two ways of assigning, say, directory level metadata to anything in the ac01ne directory from a parent directories metadata.xml.
     464                // The developers guide way: ac01ne/.*
     465                // The dls way: ac01ne
     466                // So the three tests are
     467                //System.err.println("Check for: " + (filename != null ? filename : ".*"));
     468                //System.err.println("Folder level = " + folder_level);
     469                //System.err.println("filename != null && '" + filename + "'.matches('" + filename_text + "') = " + (filename != null ? filename.matches(filename_text) : false));
     470                //System.err.println("filename != null && '" + filename + "'.matches('" + filename_text + DIRECTORY_FILENAME_SUFFIX + "') = " + (filename != null ? filename.matches(filename_text + DIRECTORY_FILENAME_SUFFIX) : false));
     471                //System.err.println("filename == null && '" + filename_text + "'.equals('.*') = " + (filename == null ? filename_text.equals(DIRECTORY_FILENAME) : false));
     472                if((filename != null && (filename.matches(filename_text) || filename.matches(filename_text)))
     473                   || (filename == null && filename_text.equals(DIRECTORY_FILENAME))) {
     474                ///ystem.err.println("Match: " + (filename != null ? filename : ".*") + " => " + filename_text);
     475                // If they match add all of the metadata found in the Description child element, remembering to abide by desired mode (accumulate vs. overwrite).
     476                NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT);
     477                for(int k = 0; k < description_elements.getLength(); k++) {
     478                    Element description_element = (Element) description_elements.item(k);
     479                    NodeList metadata_elements = description_element.getElementsByTagName(METADATA_ELEMENT);
     480                    for(int l = 0; l < metadata_elements.getLength(); l++) {
     481                    Element metadata_element = (Element) metadata_elements.item(l);
     482                    String element = metadata_element.getAttribute(NAME_ATTRIBUTE);
     483                    ///ystem.err.println("Found element: " + element);
     484                    //String language = metadata_element.getAttribute("language");
     485                    String mode = metadata_element.getAttribute(MODE_ATTRIBUTE);
     486                    // Add the new metadata to our list of metadata for this target file.
     487                    String value = Utility.stripNL(MSMUtils.getValue(metadata_element));
     488                    ///ystem.err.println("Found value: " + element);
     489                    BasicMetadata metadata = new BasicMetadata(element, value, mode.equals("accumulate"));
     490                    // If mode is overwrite, then remove any previous values for this metadata element.
     491                    if(!metadata.accumulates) {
     492                        for(int m = metadatum.size() - 1; m >= 0; m--) {
     493                        BasicMetadata old_metadata = (BasicMetadata) metadatum.get(m);
     494                        if(old_metadata.element.equals(element)) {
     495                            metadatum.remove(m);
     496                        }
     497                        old_metadata = null;
     498                        }
     499                    }
     500                    mode = null;
     501                                                     
     502                    // Add the completed metadata and clean up
     503                    metadatum.add(metadata);
     504                    metadata = null;
     505                    value = null;
     506                    element = null;
     507                    metadata_element = null;
     508                    }
     509                    metadata_elements = null;
     510                    description_element = null;
    222511                }
    223                 if(final_metadata == null) {
    224                      ///ystem.err.println("No existing Metadata object for BasicMetadata: " + basic_metadata);
    225                      // 6a. Check if an hfile is associated with this metadata, and if so load it, cache it in the collection.cfg object, then resolve metadata value index.
    226                      HFile h_file = collect_cfg.getHFile(metadata.element);
    227                      if(h_file != null && !dummy_run) {
    228                           ///ystem.err.print(metadata.value + " maps to ");
    229                           metadata.value = h_file.getValue(metadata.value);
    230                           ///ystem.err.println(metadata.value);
    231                      }
    232                      h_file = null;
    233                      // 6b. Check if there is a profile regarding the current metadata.
    234                      ///ystem.err.println("Retrieve existing action: " + collection_dir.getAbsolutePath() + ", " + metadata.element);
    235                      if(Gatherer.c_man.getCollection().msm.profiler.containsAction(collection_dir.getAbsolutePath(), metadata.element)) {
    236                           String new_element_name = Gatherer.c_man.getCollection().msm.profiler.getAction(collection_dir.getAbsolutePath(), metadata.element);
    237                           ///ystem.err.println("Profile result = " + new_element_name);
    238                           if(new_element_name == null) {
    239                                 metadata = null;
    240                           }
    241                           else {
    242                                 metadata.element = new_element_name;
    243                           }
    244                           new_element_name = null;
    245                      }
    246                      ///atherer.println("Assigning metadata.");
    247                      if(metadata != null) {
    248                           // 6c. Try to add metadata. If there is no matching metadata element:
    249                           ElementWrapper element = Gatherer.c_man.getCollection().msm.getElement(metadata.element);
    250                           // Arg. The element returned may come from the Greenstone dls, which of course should never be involved during importing. To solve check the namespace isn't "" and if it is nullify the element. Nullify. NULLIFY, Bwuhahahaha...
    251                           if(element != null && element.getNamespace().equals("")) {
    252                                 element = null;
    253                           }
    254                           // 6ci. If no match exists, prompt the user to add/merge with specific metadata element. The user can also choose to ignore this metadata.
    255                           if(element == null) {
    256                                 element = selectElement(metadata.element);
    257                                 if(!dialog_cancelled) {
    258                                      // 6ciii. If either of the above work, remember to add to profile.
    259                                      if(element == null) {
    260                                           ///ystem.err.println("Adding profile action: " + collection_dir.getAbsolutePath() + ", " + metadata.element + ", null");
    261                                           Gatherer.c_man.getCollection().msm.profiler.addAction(collection_dir.getAbsolutePath(), metadata.element, null);
    262                                      }
    263                                      else {
    264                                           ///ystem.err.println("Adding profile action: " + collection_dir.getAbsolutePath() + ", " + metadata.element + ", " + element.getName());
    265                                           Gatherer.c_man.getCollection().msm.profiler.addAction(collection_dir.getAbsolutePath(), metadata.element, element.getName());
    266                                      }
    267                                 }
    268                           }
    269                           // - Add metadata
    270                           if(!dummy_run && element != null && !dialog_cancelled) {
    271                                 ///ystem.err.println("Retrieve the value tree for " + element.toString());
    272                                 GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(element);
    273                                 if(model != null) {
    274                                      GValueNode node = model.addValue(metadata.value);
    275                                      final_metadata = new Metadata(element, node);
    276                                      ///ystem.err.println("Adding final metadata: " + metadata.toString());
    277                                      node = null;
    278                                 }
    279                                 model = null;
    280                           }
    281                           element = null;
    282                      }
    283                      // If we have successfully created a Metadata from the BasicMetadata, store it
    284                      if(final_metadata != null && !dialog_cancelled) {
    285                           transform.put(basic_metadata, new SoftReference(final_metadata));
    286                           ///ystem.err.println("Add a Metadata object for BasicMetadata: " + basic_metadata);
    287                      }
    288                 }
    289                 else {
    290                      ///ystem.err.println("Found a Metadata object for BasicMetadata: " + basic_metadata);
    291                 }
    292                 if(!dummy_run && final_metadata != null && !dialog_cancelled) {
    293                      final_metadata.setAccumulate(metadata.accumulates);
    294                      // Now we can finally add the metadata.
    295                      ///ystem.err.println("Adding Metadata: " + final_metadata);
    296                      Gatherer.c_man.getCollection().msm.fireMetadataChanged(0, destination, null, final_metadata);
    297                 }
    298                 // Otherwise there is no way to add this metadata. No value model no metadata value.
    299                 final_metadata = null;
    300                 metadata = null;
    301           }
    302      }
    303 
    304      /** Determine the different suffix between two string.
    305       * @param base_str The base <strong>String</strong>, expected to be the short of the two strings provided.
    306       * @param target_str The target <strong>String</strong>, whose differing suffix is returned.
    307       * @return A <strong>String</strong> containing the suffix from target which is different from base.
    308       */
    309      private String diff(String base_str, String target_str) {
    310           StringTokenizer base_tokenizer = new StringTokenizer(base_str, File.separator);
    311           StringTokenizer target_tokenizer = new StringTokenizer(target_str, File.separator);
    312           String base = null;
    313           String target = null;
    314           while(base_tokenizer.hasMoreTokens() && (base = base_tokenizer.nextToken()).equals((target = target_tokenizer.nextToken()))) {
    315           }
    316           StringBuffer result = new StringBuffer(target);
    317           while(target_tokenizer.hasMoreTokens()) {
    318                 result.append(File.separator);
    319                 result.append(target_tokenizer.nextToken());
    320           }
    321           return result.toString();
    322      }
    323 
    324      /** Retrieve the BasicGDMDocument found at the given file, or null if there is no such file or if it isn't a valid BasicGDMDocument. */
    325      private BasicGDMDocument getDocument(File file) {
    326           ///ystem.err.println("Get Document at: " + file.getAbsolutePath());
    327           BasicGDMDocument document = null;
    328           if(!ignore_list.contains(file) && file.exists()) {
    329                 // Check cache
    330                 SoftReference reference = (SoftReference) get(file);
    331                 if(reference != null) {
    332                      ///ystem.err.println("Hit!!");
    333                      document = (BasicGDMDocument) reference.get();
    334                      reference = null;
    335                 }
    336                 // If that didn't work try to parse in the document
    337                 if(document == null) {
    338                      ///ystem.err.println("Miss or stale reference.");
    339                      document = new BasicGDMDocument(file);
    340                      if(document.isValid()) {
    341                           put(file, new SoftReference(document));
    342                      }
    343                      else {
    344                           ///ystem.err.println(file.getAbsolutePath() + " is not a valid GDM XML file.");
    345                           ignore_list.add(file);
    346                           document = null;
    347                      }
    348                 }
    349           }
    350           else {
    351                 ///ystem.err.println("Ignoring file or file doesn't exists.");
    352           }
    353           return document;
    354      }
    355 
    356 
    357      /** Display a prompt allowing a user to select a metadata element to attempt to force add/merge or ignore a metadata element to. For instance an old version of a metadata.xml from the DLS collection might have an assigned metadata value "Publisher=EC Courier", however Publisher won't automatically match to any metadata set. This prompt will be displayed, and some effort will be made to systematically locate the appropriate set. In this case this should be the DLS metadata set as dls.Publisher should be the closest match. Regardless the element selected is returned.
    358       * @param element_name The name of the element we are trying to add, as a <strong>String</strong>.
    359       * @return The <strong>ElementWrapper</strong> choosen by the user, or <i>null</i> to skip this metadata element.
    360       */
    361      private ElementWrapper selectElement(String element_name) {
    362           ElementWrapper result = Gatherer.c_man.getCollection().msm.prompt.selectElement(element_name);
    363           dialog_cancelled = Gatherer.c_man.getCollection().msm.prompt.wasDialogCancelled();
    364           return result;
    365      }
    366 
    367      /** A 'basic' version of the more complete GDMDocument used elsewhere, this object provides the same functionality except that it doesn't use Metadata objects. These objects require live references to elements within the MetadataSetManager and GValueModels, but these may not yet exist (and indeed may never exist) for metadata parsed from metadata.xml's outside of our current collection. Thus this class returns a String (or an ArrayList of Strings) when asked for the metadata associated with a certain file. Also notice that this class provides no constructor method for creating a blank document, nor does it ever need a reference to the Gatherer.*/
    368      private class BasicGDMDocument
    369           extends HashMap {
    370           /** The document this class sources its data from. */
    371           private Document base_document;
    372           /** This constructor takes the original document and parsed out and stores metadata with its association to filenames. */
    373           public BasicGDMDocument(File file) {
    374                 ///ystem.err.println("New BasicGDMDocument: " + file.getAbsolutePath());
    375                 base_document = Utility.parse(file.getAbsolutePath(), false);
    376           }
    377           /** Retrieve all of the metadata in this file. */
    378           public ArrayList getAllMetadata() {
    379                 ArrayList metadatum = new ArrayList();
    380                 // Don't search the cache as this would never have been added.
    381                 try {
    382                      // Retrieve the document element.
    383                      Element directorymetadata_element = base_document.getDocumentElement();
    384                      // Iterate through the filesets, checking the FileName child element against   the target file's name using regular expression matching.
    385                      NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT);
    386                      for(int i = 0; i < fileset_elements.getLength(); i++) {
    387                           Element fileset_element = (Element) fileset_elements.item(i);
    388                           NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT);
    389                           for(int j = 0; j < filename_elements.getLength(); j++) {
    390                                 Element filename_element = (Element) filename_elements.item(j);
    391                                 // If they match add all of the metadata found in the Description child element, overwriting any metadata with the same element
    392                                 NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT);
    393                                 for(int k = 0; k < description_elements.getLength(); k++) {
    394                                      Element description_element = (Element) description_elements.item(k);
    395                                      NodeList metadata_elements = description_element.getElementsByTagName(METADATA_ELEMENT);
    396                                      for(int l = 0; l < metadata_elements.getLength(); l++) {
    397                                           Element metadata_element = (Element) metadata_elements.item(l);
    398                                           String element = metadata_element.getAttribute(NAME_ATTRIBUTE);
    399                                           BasicMetadata metadata = new BasicMetadata(element, Utility.METADATA_XML, true);
    400                                           // Remove any previous values for this metadata element.
    401                                           for(int m = metadatum.size() - 1; m >= 0; m--) {
    402                                                 BasicMetadata old_metadata = (BasicMetadata) metadatum.get(m);
    403                                                 if(old_metadata.element.equals(element)) {
    404                                                      metadatum.remove(m);
    405                                                 }
    406                                                 old_metadata = null;
    407                                           }
    408                                           // Add the completed metadata and clean up
    409                                           metadatum.add(metadata);
    410                                           metadata = null;
    411                                           element = null;
    412                                           metadata_element = null;
    413                                      }
    414                                      metadata_elements = null;
    415                                      description_element = null;
    416                                 }
    417                                 description_elements = null;
    418                                 filename_element = null;
    419                           }
    420                           filename_elements = null;
    421                           fileset_element = null;
    422                      }
    423                      fileset_elements = null;
    424                      directorymetadata_element = null;
    425                 }
    426                 catch (Exception error) {
    427                      Gatherer.self.printStackTrace(error);
    428                 }
    429                 return metadatum;
    430           }
     512                description_elements = null;
     513                }
     514                else {
     515                ///ystem.err.println("No Match!");
     516                }
     517                filename_text = null;
     518                filename_element = null;
     519            }
     520            filename_elements = null;
     521            fileset_element = null;
     522            }
     523            fileset_elements = null;
     524            directorymetadata_element = null;
     525        }
     526        catch (Exception error) {
     527            Gatherer.self.printStackTrace(error);
     528        }
     529        // Cache the result, given that these external metadata.xmls are taken to be static at the time of reading (if you happen to be sourcing information from a opened collection that someone is working on, too bad.
     530        if(filename != null) {
     531            put(filename, metadatum);
     532        }
     533        else {
     534            put(DIRECTORY_FILENAME, metadatum);
     535        }
     536        }
     537        return metadatum;
     538    }
    431539         
    432           /** Retrieve any metadata associated with a certain file. If filename is null we are attempting to find directory level metadata. */
    433           public ArrayList getMetadata(String filename, ArrayList metadatum_so_far, boolean folder_level) {
    434                 ///ystem.err.println("Retrieving metadata for: " + filename);
    435                 ArrayList metadatum = null;
    436                 // We start by attempting to retrieve this metadata from the cache.
    437                 if(filename != null) {
    438                      metadatum = (ArrayList) get(filename);
    439                 }
    440                 else {
    441                      metadatum = (ArrayList) get(DIRECTORY_FILENAME);
    442                 }
    443                 // If that failed we consult the document for metadata.
    444                 if(metadatum == null) {
    445                      metadatum = new ArrayList();
    446                      if(metadatum_so_far == null) {
    447                           metadatum = new ArrayList();
    448                      }
    449                      else {
    450                           metadatum = metadatum_so_far;
    451                      }
    452                      try {
    453                           // Retrieve the document element.
    454                           Element directorymetadata_element = base_document.getDocumentElement();
    455                           // Iterate through the filesets, checking the FileName child element against  the target file's name using regular expression matching.
    456                           NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT);
    457                           for(int i = 0; i < fileset_elements.getLength(); i++) {
    458                                 Element fileset_element = (Element) fileset_elements.item(i);
    459                                 NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT);
    460                                 for(int j = 0; j < filename_elements.getLength(); j++) {
    461                                      Element filename_element = (Element) filename_elements.item(j);
    462                                      String filename_text = MSMUtils.getValue(filename_element);
    463                                      // Crappy. There are apparently two ways of assigning, say, directory level metadata to anything in the ac01ne directory from a parent directories metadata.xml.
    464                                      // The developers guide way: ac01ne/.*
    465                                      // The dls way: ac01ne
    466                                      // So the three tests are
    467                                      //System.err.println("Check for: " + (filename != null ? filename : ".*"));
    468                                      //System.err.println("Folder level = " + folder_level);
    469                                      //System.err.println("filename != null && '" + filename + "'.matches('" + filename_text + "') = " + (filename != null ? filename.matches(filename_text) : false));
    470                                      //System.err.println("filename != null && '" + filename + "'.matches('" + filename_text + DIRECTORY_FILENAME_SUFFIX + "') = " + (filename != null ? filename.matches(filename_text + DIRECTORY_FILENAME_SUFFIX) : false));
    471                                      //System.err.println("filename == null && '" + filename_text + "'.equals('.*') = " + (filename == null ? filename_text.equals(DIRECTORY_FILENAME) : false));
    472                                      if((filename != null && (filename.matches(filename_text) || filename.matches(filename_text)))
    473                                          || (filename == null && filename_text.equals(DIRECTORY_FILENAME))) {
    474                                           ///ystem.err.println("Match: " + (filename != null ? filename : ".*") + " => " + filename_text);
    475                                           // If they match add all of the metadata found in the Description child element, remembering to abide by desired mode (accumulate vs. overwrite).
    476                                           NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT);
    477                                           for(int k = 0; k < description_elements.getLength(); k++) {
    478                                                 Element description_element = (Element) description_elements.item(k);
    479                                                 NodeList metadata_elements = description_element.getElementsByTagName(METADATA_ELEMENT);
    480                                                 for(int l = 0; l < metadata_elements.getLength(); l++) {
    481                                                      Element metadata_element = (Element) metadata_elements.item(l);
    482                                                      String element = metadata_element.getAttribute(NAME_ATTRIBUTE);
    483                                                      ///ystem.err.println("Found element: " + element);
    484                                                      //String language = metadata_element.getAttribute("language");
    485                                                      String mode = metadata_element.getAttribute(MODE_ATTRIBUTE);
    486                                                      // Add the new metadata to our list of metadata for this target file.
    487                                                      String value = Utility.stripNL(MSMUtils.getValue(metadata_element));
    488                                                      ///ystem.err.println("Found value: " + element);
    489                                                      BasicMetadata metadata = new BasicMetadata(element, value, mode.equals("accumulate"));
    490                                                      // If mode is overwrite, then remove any previous values for this metadata element.
    491                                                      if(!metadata.accumulates) {
    492                                                           for(int m = metadatum.size() - 1; m >= 0; m--) {
    493                                                                 BasicMetadata old_metadata = (BasicMetadata) metadatum.get(m);
    494                                                                 if(old_metadata.element.equals(element)) {
    495                                                                      metadatum.remove(m);
    496                                                                 }
    497                                                                 old_metadata = null;
    498                                                           }
    499                                                      }
    500                                                      mode = null;
    501                                                      
    502                                                      // Add the completed metadata and clean up
    503                                                      metadatum.add(metadata);
    504                                                      metadata = null;
    505                                                      value = null;
    506                                                      element = null;
    507                                                      metadata_element = null;
    508                                                 }
    509                                                 metadata_elements = null;
    510                                                 description_element = null;
    511                                           }
    512                                           description_elements = null;
    513                                      }
    514                                      else {
    515                                           ///ystem.err.println("No Match!");
    516                                      }
    517                                      filename_text = null;
    518                                      filename_element = null;
    519                                 }
    520                                 filename_elements = null;
    521                                 fileset_element = null;
    522                           }
    523                           fileset_elements = null;
    524                           directorymetadata_element = null;
    525                      }
    526                      catch (Exception error) {
    527                           Gatherer.self.printStackTrace(error);
    528                      }
    529                      // Cache the result, given that these external metadata.xmls are taken to be static at the time of reading (if you happen to be sourcing information from a opened collection that someone is working on, too bad.
    530                      if(filename != null) {
    531                           put(filename, metadatum);
    532                      }
    533                      else {
    534                           put(DIRECTORY_FILENAME, metadatum);
    535                      }
    536                 }
    537                 return metadatum;
    538           }
    539          
    540           /** Determine is this is a valid Greenstone Directory Metadata file. It may of course just be some xml file with the name metadata.xml. */
    541           public boolean isValid() {
     540    /** Determine is this is a valid Greenstone Directory Metadata file. It may of course just be some xml file with the name metadata.xml. */
     541    public boolean isValid() {
    542542                // Just determine if the doctype is GreenstoneDirectoryMetadata and root node is called DirectoryMetadata.
    543                 String doctype_name = base_document.getDoctype().getName();
    544                 String root_name = base_document.getDocumentElement().getTagName();
    545                 return ((doctype_name.equals("DirectoryMetadata") || doctype_name.equals("GreenstoneDirectoryMetadata")) && (root_name.equals("DirectoryMetadata") || root_name.equals("GreenstoneDirectoryMetadata")));
    546           }
    547 
    548           /** Decode a string that was previously made Perl safe.
    549             * @param safe The encoded <strong>String</strong> where dangerous characters have been escaped.
    550             * @return A <strong>String</strong> with all the escaping removed.
    551             */
    552           private String decode(String safe) {
    553                 String dangerous = safe.replaceAll("\\\\.",".");
    554                 return dangerous;
    555           }
    556     }
    557     /** A simplistic version of metadata, with no live references. */
    558     private class BasicMetadata
    559           implements Comparable {
    560           public boolean accumulates;
    561           /** The collection this metadata was extracted from. Important when attempting to map BasicMetadata to its Metadata incarnation. */
    562           public File collection;
    563           /** The metadata element. */
    564           public String element = null;
    565           /** The value. */
    566           public String value = null;
    567           /** Constructor takes initial values for element and value.
    568             * @param element The metadata element as a <strong>String</strong>.
    569             * @param value The value as a <strong>String</strong>.
    570             */
    571           public BasicMetadata(String element, String value, boolean accumulates) {
    572                 this.accumulates = accumulates;
    573                 this.element = element;
    574                 this.value = value;
    575           }
    576 
    577           public int compareTo(Object other) {
    578                 return toString().compareTo(other.toString());
    579           }
    580           /** Compare two BasicMetadata objects for equality.
    581             * @param object The other <strong>Object</strong>.
    582             * @return <i>true</i> if this BasicMetadata matches the given object, <i>false</i> otherwise.
    583             */
    584           public boolean equals(Object object) {
    585                 BasicMetadata other = (BasicMetadata) object;
    586                 if(collection != null) {
    587                      return (collection.equals(other.collection) && element.equals(other.element) && value.equals(other.value));
    588                 }
    589                 return (element.equals(other.element) && value.equals(other.value));
    590           }
    591           public String toString() {
    592                 return element + " = " + value;
    593           }
    594     }
    595 
    596     /** This class provides a cache for the instances of parsed collect.cfg files and their associated data. Assures that the most recently cached CollectCFG will remain available. Older objects are maintained as soft references and are freed at the JVM implementations descretion, but are gareunteed to be garbage collected before an OutOfMemory exception is thrown. */
    597     private class CollectCFGCache
    598           extends LinkedHashMap {
    599           /** Retrieve the CollectCFG object that matches the given collection file path.
    600             * @param collection_file The <strong>File</strong> that references the collection's directory.
    601             * @return The <strong>CollectCFG</strong> that belongs to this collection, or <i>null</i> if no such file exists (so we probably aren't in a collection!).
    602             */
    603           public CollectCFG get(File collect_cfg_file) {
     543        String doctype_name = base_document.getDoctype().getName();
     544        String root_name = base_document.getDocumentElement().getTagName();
     545        return ((doctype_name.equals("DirectoryMetadata") || doctype_name.equals("GreenstoneDirectoryMetadata")) && (root_name.equals("DirectoryMetadata") || root_name.equals("GreenstoneDirectoryMetadata")));
     546    }
     547
     548    /** Decode a string that was previously made Perl safe.
     549     * @param safe The encoded <strong>String</strong> where dangerous characters have been escaped.
     550     * @return A <strong>String</strong> with all the escaping removed.
     551     */
     552    private String decode(String safe) {
     553        String dangerous = safe.replaceAll("\\\\.",".");
     554        return dangerous;
     555    }
     556    }
     557    /** A simplistic version of metadata, with no live references. */
     558    private class BasicMetadata
     559    implements Comparable {
     560    public boolean accumulates;
     561    /** The collection this metadata was extracted from. Important when attempting to map BasicMetadata to its Metadata incarnation. */
     562    public File collection;
     563    /** The metadata element. */
     564    public String element = null;
     565    /** The value. */
     566    public String value = null;
     567    /** Constructor takes initial values for element and value.
     568     * @param element The metadata element as a <strong>String</strong>.
     569     * @param value The value as a <strong>String</strong>.
     570     */
     571    public BasicMetadata(String element, String value, boolean accumulates) {
     572        this.accumulates = accumulates;
     573        this.element = element;
     574        this.value = value;
     575    }
     576
     577    public int compareTo(Object other) {
     578        return toString().compareTo(other.toString());
     579    }
     580    /** Compare two BasicMetadata objects for equality.
     581     * @param object The other <strong>Object</strong>.
     582     * @return <i>true</i> if this BasicMetadata matches the given object, <i>false</i> otherwise.
     583     */
     584    public boolean equals(Object object) {
     585        BasicMetadata other = (BasicMetadata) object;
     586        if(collection != null) {
     587        return (collection.equals(other.collection) && element.equals(other.element) && value.equals(other.value));
     588        }
     589        return (element.equals(other.element) && value.equals(other.value));
     590    }
     591    public String toString() {
     592        return element + " = " + value;
     593    }
     594    }
     595
     596    /** This class provides a cache for the instances of parsed collect.cfg files and their associated data. Assures that the most recently cached CollectCFG will remain available. Older objects are maintained as soft references and are freed at the JVM implementations descretion, but are gareunteed to be garbage collected before an OutOfMemory exception is thrown. */
     597    private class CollectCFGCache
     598    extends LinkedHashMap {
     599    /** Retrieve the CollectCFG object that matches the given collection file path.
     600     * @param collection_file The <strong>File</strong> that references the collection's directory.
     601     * @return The <strong>CollectCFG</strong> that belongs to this collection, or <i>null</i> if no such file exists (so we probably aren't in a collection!).
     602     */
     603    public CollectCFG get(File collect_cfg_file) {
    604604                ///ystem.err.println("Retrieve the collection configuration file at: " + collect_cfg_file);
    605                 CollectCFG collect_cfg = null;
     605        CollectCFG collect_cfg = null;
    606606                // Attempt to load from cache.
    607                 SoftReference reference = (SoftReference) super.get(collect_cfg_file);
     607        SoftReference reference = (SoftReference) super.get(collect_cfg_file);
    608608                // If is doesn't exist, either because its never been loaded, or thats its cache reference has gone stale, attempt to load it again.
    609                 if(reference == null || (collect_cfg = (CollectCFG)reference.get()) == null) {
    610                      try {
    611                           collect_cfg = new CollectCFG(collect_cfg_file);
    612                           put(collect_cfg_file, new SoftReference(collect_cfg));
    613                      }
    614                      catch(Exception error) {
    615                           Gatherer.printStackTrace(error);
    616                           collect_cfg = null;
    617                      }
    618                 }
    619                 return collect_cfg;
    620           }
    621 
    622           protected boolean removeEldestEntry(java.util.Map.Entry entry) {
    623                 return (size() > MAX_CFG_CACHE_SIZE);
    624           }
    625     }
    626 
    627     /** The CollectCFG object encapsulates important metadata information extracted from a collect.cfg file, such as required metadata sets, and hfile associations. As the former are merged, their references are removed from this object, whereas the for the later references are replaced a representation of the hfile itself. */
    628     private class CollectCFG {
    629           /** A list of the metadata sets associated with the collect.cfg file. */
    630           private ArrayList metadatasets = null;
    631           /** A hash mapping from metadata element name to hierarchy file, or possibly hierarchy object. */
    632           private HashMap hfiles = null;
    633           /** The token at the start of a classify command line within the collect.cfg. */
    634           static final private String CLASSIFY_COMMAND = "classify";
    635           /** The token at the start of a metadataset command line within the collect.cfg. */
    636           static final private String METADATASET_COMMAND = "metadataset";
    637           /** Constructor which takes a file assumed to be the location of a collect.cfg file belonging to a Greenstone Collection.
    638             * @param file A <strong>File</strong> referencing a collect.cfg file.
    639             */
    640           public CollectCFG(File file)
    641                 throws Exception {
     609        if(reference == null || (collect_cfg = (CollectCFG)reference.get()) == null) {
     610        try {
     611            collect_cfg = new CollectCFG(collect_cfg_file);
     612            put(collect_cfg_file, new SoftReference(collect_cfg));
     613        }
     614        catch(Exception error) {
     615            Gatherer.printStackTrace(error);
     616            collect_cfg = null;
     617        }
     618        }
     619        return collect_cfg;
     620    }
     621
     622    protected boolean removeEldestEntry(java.util.Map.Entry entry) {
     623        return (size() > MAX_CFG_CACHE_SIZE);
     624    }
     625    }
     626
     627    /** The CollectCFG object encapsulates important metadata information extracted from a collect.cfg file, such as required metadata sets, and hfile associations. As the former are merged, their references are removed from this object, whereas the for the later references are replaced a representation of the hfile itself. */
     628    private class CollectCFG {
     629    /** A list of the metadata sets associated with the collect.cfg file. */
     630    private ArrayList metadatasets = null;
     631    /** A hash mapping from metadata element name to hierarchy file, or possibly hierarchy object. */
     632    private HashMap hfiles = null;
     633    /** The token at the start of a classify command line within the collect.cfg. */
     634    static final private String CLASSIFY_COMMAND = "classify";
     635    /** The token at the start of a metadataset command line within the collect.cfg. */
     636    static final private String METADATASET_COMMAND = "metadataset";
     637    /** Constructor which takes a file assumed to be the location of a collect.cfg file belonging to a Greenstone Collection.
     638     * @param file A <strong>File</strong> referencing a collect.cfg file.
     639     */
     640    public CollectCFG(File file)
     641        throws Exception {
    642642                ///atherer.println("Loading a new collection configuration file: " + file.getAbsolutePath());
    643                 File etc_directory = file.getParentFile();
    644                 hfiles = new HashMap();
    645                 metadatasets = new ArrayList();
    646                 FileReader reader = new FileReader(file);
    647                 BufferedReader in = new BufferedReader(reader);
    648                 String command = null;
    649                 while((command = in.readLine()) != null) {
    650                      CommandTokenizer tokenizer = new CommandTokenizer(command);
    651                      if(tokenizer.hasMoreTokens()) {
    652                           String token = tokenizer.nextToken().toLowerCase();
    653                           if(token.equals(METADATASET_COMMAND)) {
    654                                 String family_name = tokenizer.nextToken();
    655                                 String file_str = tokenizer.nextToken();
    656                                 if(file_str.startsWith("\"") && file_str.endsWith("\"") && !file_str.equals("\"\"")) {
    657                                     file_str = file_str.substring(1, file_str.length() - 1);
    658                                 }
    659                                 // If the file str is -only- the filename then we add <col_dir>/metadata/
    660                                 File mds_file = null;
    661                                 if(file_str.indexOf(File.separator) == -1) {
    662                                     mds_file = new File(file.getParentFile().getParentFile(), File.separator + "metadata" + File.separator + file_str);
    663                                 }
    664                                 else {
    665                                     mds_file = new File(file_str);
    666                                 }
    667                                 ///ystem.err.println("Attempting to file mds file at " + file.getAbsolutePath());
    668                                 if(mds_file.exists()) {
    669                                     metadatasets.add(mds_file);
    670                                 }
    671                                 mds_file = null;
    672                                 file_str = null;
    673                                 family_name = null;
    674                           }
    675                           // Also look for any classify commands that include an hfile and element
    676                           else if(token.equals(CLASSIFY_COMMAND)) {
    677                                 String hfile_name = null;
    678                                 String element_name = null;
    679                                 // Drop the classifier name
    680                                 tokenizer.nextToken();
    681                                 while(tokenizer.hasMoreTokens()) {
    682                                     token = tokenizer.nextToken().toLowerCase();
    683                                     if(token.equals("-hfile")) {
    684                                           hfile_name = tokenizer.nextToken();
    685                                     }
    686                                     else if(token.equals("-metadata")) {
    687                                           element_name = tokenizer.nextToken();
    688                                     }
    689                                 }
    690                                 if(hfile_name != null && element_name != null) {
    691                                     // If hfile_name has no path, append the etc directories one. Either way create a file reference
    692                                     File hfile = null;
    693                                     hfile_name = hfile_name.replace('\\', File.separatorChar);
    694                                     hfile_name = hfile_name.replace('/', File.separatorChar);
    695                                     if(hfile_name.indexOf(File.separator) == -1) {
    696                                           hfile = new File(etc_directory, hfile_name);
    697                                     }
    698                                     else {
    699                                           hfile = new File(hfile_name);
    700                                     }
    701                                     // Add to hfiles
    702                                     ///atherer.println("Adding hfile reference: " + element_name + " -> " + hfile);
    703                                     hfiles.put(element_name, hfile);
    704                                     hfile = null;
    705                                 }
    706                                 element_name = null;
    707                                 hfile_name = null;
    708                           }
    709                           tokenizer = null;
    710                      }
    711                 }
    712                 command = null;
    713                 in.close();
    714                 reader.close();
    715                 in = null;
    716                 reader = null;
     643        File etc_directory = file.getParentFile();
     644        hfiles = new HashMap();
     645        metadatasets = new ArrayList();
     646        FileReader reader = new FileReader(file);
     647        BufferedReader in = new BufferedReader(reader);
     648        String command = null;
     649        while((command = in.readLine()) != null) {
     650        CommandTokenizer tokenizer = new CommandTokenizer(command);
     651        if(tokenizer.hasMoreTokens()) {
     652            String token = tokenizer.nextToken().toLowerCase();
     653            if(token.equals(METADATASET_COMMAND)) {
     654            String family_name = tokenizer.nextToken();
     655            String file_str = tokenizer.nextToken();
     656            if(file_str.startsWith("\"") && file_str.endsWith("\"") && !file_str.equals("\"\"")) {
     657                file_str = file_str.substring(1, file_str.length() - 1);
     658            }
     659            // If the file str is -only- the filename then we add <col_dir>/metadata/
     660            File mds_file = null;
     661            if(file_str.indexOf(File.separator) == -1) {
     662                mds_file = new File(file.getParentFile().getParentFile(), File.separator + "metadata" + File.separator + file_str);
     663            }
     664            else {
     665                mds_file = new File(file_str);
     666            }
     667            ///ystem.err.println("Attempting to file mds file at " + file.getAbsolutePath());
     668            if(mds_file.exists()) {
     669                metadatasets.add(mds_file);
     670            }
     671            mds_file = null;
     672            file_str = null;
     673            family_name = null;
     674            }
     675            // Also look for any classify commands that include an hfile and element
     676            else if(token.equals(CLASSIFY_COMMAND)) {
     677            String hfile_name = null;
     678            String element_name = null;
     679            // Drop the classifier name
     680            tokenizer.nextToken();
     681            while(tokenizer.hasMoreTokens()) {
     682                token = tokenizer.nextToken().toLowerCase();
     683                if(token.equals("-hfile")) {
     684                hfile_name = tokenizer.nextToken();
     685                }
     686                else if(token.equals("-metadata")) {
     687                element_name = tokenizer.nextToken();
     688                }
     689            }
     690            if(hfile_name != null && element_name != null) {
     691                // If hfile_name has no path, append the etc directories one. Either way create a file reference
     692                File hfile = null;
     693                hfile_name = hfile_name.replace('\\', File.separatorChar);
     694                hfile_name = hfile_name.replace('/', File.separatorChar);
     695                if(hfile_name.indexOf(File.separator) == -1) {
     696                hfile = new File(etc_directory, hfile_name);
     697                }
     698                else {
     699                hfile = new File(hfile_name);
     700                }
     701                // Add to hfiles
     702                ///atherer.println("Adding hfile reference: " + element_name + " -> " + hfile);
     703                hfiles.put(element_name, hfile);
     704                hfile = null;
     705            }
     706            element_name = null;
     707            hfile_name = null;
     708            }
     709            tokenizer = null;
     710        }
     711        }
     712        command = null;
     713        in.close();
     714        reader.close();
     715        in = null;
     716        reader = null;
    717717                // Now we search the etc directory for *.txt files which we attempt to parse as hfiles
    718                 File children[] = etc_directory.listFiles(); // We are sure there is at least one, collect.cfg
    719                 for(int i = 0; i < children.length; i++) {
    720                      // If this is a text file, extract the element name and process
    721                      String name = children[i].getName();
    722                      if(children[i].isFile() && name.endsWith(".txt")) {
    723                           String element_name = name.substring(0, name.lastIndexOf("."));
    724                           if(!hfiles.containsKey(element_name)) {
    725                                 ///atherer.println("Adding hfile reference: " + element_name + " -> " + children[i]);
    726                                 hfiles.put(element_name, children[i]);
    727                           }
    728                           element_name = null;
    729                      }
    730                      name = null;
    731                 }
    732                 children = null;
    733                 etc_directory = null;
    734                 file = null;
    735           }
    736           /** Attempts to retrieve the HFile object associated with a certain metadata element. This may have already been cached, or may need to be loaded. Then again it may not even be necessary.
    737             * @param element The fully qualified name of a metadata element, as a <strong>String</strong>.
    738             * @return The <strong>HFile</strong> associated with the given element, or <i>null</i> if its unnecessary.
    739             * @see org.greenstone.gatherer.cdm.CommandTokenizer
    740             */
    741           public HFile getHFile(String element) {
    742                 HFile result = null;
    743                 Object target = hfiles.get(element);
     718        File children[] = etc_directory.listFiles(); // We are sure there is at least one, collect.cfg
     719        for(int i = 0; i < children.length; i++) {
     720        // If this is a text file, extract the element name and process
     721        String name = children[i].getName();
     722        if(children[i].isFile() && name.endsWith(".txt")) {
     723            String element_name = name.substring(0, name.lastIndexOf("."));
     724            if(!hfiles.containsKey(element_name)) {
     725            ///atherer.println("Adding hfile reference: " + element_name + " -> " + children[i]);
     726            hfiles.put(element_name, children[i]);
     727            }
     728            element_name = null;
     729        }
     730        name = null;
     731        }
     732        children = null;
     733        etc_directory = null;
     734        file = null;
     735    }
     736    /** Attempts to retrieve the HFile object associated with a certain metadata element. This may have already been cached, or may need to be loaded. Then again it may not even be necessary.
     737     * @param element The fully qualified name of a metadata element, as a <strong>String</strong>.
     738     * @return The <strong>HFile</strong> associated with the given element, or <i>null</i> if its unnecessary.
     739     * @see org.greenstone.gatherer.cdm.CommandTokenizer
     740     */
     741    public HFile getHFile(String element) {
     742        HFile result = null;
     743        Object target = hfiles.get(element);
    744744                // If target is non-null
    745                 if(target != null) {
    746                      // If we haven't already load and parse the file.
    747                      if(target instanceof File) {
    748                           ///ystem.err.println("\nHFILE-MISS!! Loading " + target.toString());
    749                           result = new HFile();
    750                           try {
    751                                 FileReader in_filereader = new FileReader((File)target);
    752                                 //DecodeHTMLReader in_decodehtmlreader = new DecodeHTMLReader(in_filereader);
    753                                 BufferedReader in = new BufferedReader(in_filereader);
    754                                 String line = null;
    755                                 while((line = in.readLine()) != null) {                             
    756                                     CommandTokenizer tokenizer = new CommandTokenizer(line);
    757                                     String alias = Utility.decodeGreenstone(tokenizer.nextToken());
    758                                     String index = tokenizer.nextToken();
    759                                     String value = Utility.decodeGreenstone(tokenizer.nextToken());
    760                                     ///ystem.err.println("Read " + index + ", " + alias + ", " + value);
    761                                     if(alias.startsWith("\"") && alias.endsWith("\"") && !alias.equals("\"\"")) {
    762                                           alias = alias.substring(1, alias.length() - 1);
    763                                     }
    764                                     if(value.startsWith("\"") && value.endsWith("\"") && !value.equals("\"\"")) {
    765                                           value = value.substring(1, value.length() - 1);
    766                                     }
    767                                     result.add(index, alias, value);
    768                                     value = null;
    769                                     alias = null;
    770                                     index = null;
    771                                     tokenizer = null;
    772                                 }
    773                                 line = null;
    774                                 in.close();
    775                                 in = null;
    776                                 //in_decodehtmlreader = null;
    777                                 in_filereader = null;
    778                                 hfiles.put(element, result);
    779                           }
    780                           catch (Exception error) {
    781                                 error.printStackTrace();
    782                                 hfiles.remove(element);
    783                           }
    784                      }
    785                      else {
    786                           ///ystem.err.print("HFILE-HIT!!! ");
    787                           result = (HFile) target;
    788                      }
    789                 }
     745        if(target != null) {
     746        // If we haven't already load and parse the file.
     747        if(target instanceof File) {
     748            ///ystem.err.println("\nHFILE-MISS!! Loading " + target.toString());
     749            result = new HFile();
     750            try {
     751            FileReader in_filereader = new FileReader((File)target);
     752            //DecodeHTMLReader in_decodehtmlreader = new DecodeHTMLReader(in_filereader);
     753            BufferedReader in = new BufferedReader(in_filereader);
     754            String line = null;
     755            while((line = in.readLine()) != null) {                             
     756                CommandTokenizer tokenizer = new CommandTokenizer(line);
     757                String alias = Utility.decodeGreenstone(tokenizer.nextToken());
     758                String index = tokenizer.nextToken();
     759                String value = Utility.decodeGreenstone(tokenizer.nextToken());
     760                ///ystem.err.println("Read " + index + ", " + alias + ", " + value);
     761                if(alias.startsWith("\"") && alias.endsWith("\"") && !alias.equals("\"\"")) {
     762                alias = alias.substring(1, alias.length() - 1);
     763                }
     764                if(value.startsWith("\"") && value.endsWith("\"") && !value.equals("\"\"")) {
     765                value = value.substring(1, value.length() - 1);
     766                }
     767                result.add(index, alias, value);
     768                value = null;
     769                alias = null;
     770                index = null;
     771                tokenizer = null;
     772            }
     773            line = null;
     774            in.close();
     775            in = null;
     776            //in_decodehtmlreader = null;
     777            in_filereader = null;
     778            hfiles.put(element, result);
     779            }
     780            catch (Exception error) {
     781            error.printStackTrace();
     782            hfiles.remove(element);
     783            }
     784        }
     785        else {
     786            ///ystem.err.print("HFILE-HIT!!! ");
     787            result = (HFile) target;
     788        }
     789        }
    790790                // Else no hfile is needed for this element
    791                 target = null;
    792                 return result;
    793           }
    794           /** Retrieve the list of metadata sets associated with this collection.
    795             * @return An <strong>ArrayList</strong> of metadata set Files.
    796             */
    797           public ArrayList getMetadataSets() {
    798                 return metadatasets;
    799           }
    800     }
    801 
    802     /** The HFile object provides a container for the mappings from indexes, of the form 1.1.1, to alias-value pairs. It also provides method to retrieving the alias and value for a certain element, remembering that values must be expressed in terms of their absolute subject heirarchy path. */
    803     private class HFile
    804           extends HashMap {
    805           /** Construct a new HFile object with no initial values. */
    806           public HFile() {
    807                 super();
    808           }
    809           /** Add a new (index,(alias, value)) mapping.
    810             * @param index The index of this mapping as a <strong>String</strong>.
    811             * @param alias The alias of this mapping as a <strong>String</strong>.
    812             * @param value And finally the value of this mapping as a, you guessed it, <strong>String</strong>.
    813             */
    814           public void add(String index, String alias, String value) {
    815                 Entry entry = new Entry(index, alias, value);
     791        target = null;
     792        return result;
     793    }
     794    /** Retrieve the list of metadata sets associated with this collection.
     795     * @return An <strong>ArrayList</strong> of metadata set Files.
     796     */
     797    public ArrayList getMetadataSets() {
     798        return metadatasets;
     799    }
     800    }
     801
     802    /** The HFile object provides a container for the mappings from indexes, of the form 1.1.1, to alias-value pairs. It also provides method to retrieving the alias and value for a certain element, remembering that values must be expressed in terms of their absolute subject heirarchy path. */
     803    private class HFile
     804    extends HashMap {
     805    /** Construct a new HFile object with no initial values. */
     806    public HFile() {
     807        super();
     808    }
     809    /** Add a new (index,(alias, value)) mapping.
     810     * @param index The index of this mapping as a <strong>String</strong>.
     811     * @param alias The alias of this mapping as a <strong>String</strong>.
     812     * @param value And finally the value of this mapping as a, you guessed it, <strong>String</strong>.
     813     */
     814    public void add(String index, String alias, String value) {
     815        Entry entry = new Entry(index, alias, value);
    816816                ///ystem.err.println("Adding entry: " + index + " \"" + alias + "\" \"" + value + "\"");
    817                 put(index, entry);
    818                 put(alias, entry);
    819           }
    820           public String getAlias(String index) {
    821                 String alias = "";
    822                 Entry entry = (Entry) get(index);
    823                 if(entry != null) {
    824                      alias = entry.alias;
    825                 }
    826                 entry = null;
    827                 return alias;
    828           }
    829           /** Retrieve the value associated with a certain index. This is harder than it first sounds as you must take into account the parent indexes of this one.
    830             * @param index The index whose value you wish to calculate, as a <strong>String</strong>.
    831             * @return The fully quantified path to the value that matches index, also as a <strong>String</strong>. Delimitiation between subject layers is denoted by the character '/'
    832             */
    833           public String getValue(String index) {
     817        put(index, entry);
     818        put(alias, entry);
     819    }
     820    public String getAlias(String index) {
     821        String alias = "";
     822        Entry entry = (Entry) get(index);
     823        if(entry != null) {
     824        alias = entry.alias;
     825        }
     826        entry = null;
     827        return alias;
     828    }
     829    /** Retrieve the value associated with a certain index. This is harder than it first sounds as you must take into account the parent indexes of this one.
     830     * @param index The index whose value you wish to calculate, as a <strong>String</strong>.
     831     * @return The fully quantified path to the value that matches index, also as a <strong>String</strong>. Delimitiation between subject layers is denoted by the character '/'
     832     */
     833    public String getValue(String index) {
    834834                ///ystem.err.println("Retrieve value for the alias/index: '" + index + "'");
    835                 StringBuffer value = new StringBuffer("");
     835        StringBuffer value = new StringBuffer("");
    836836                // If index isn't the index, it must be the alias. Replace it with the index dammit.
    837                 Entry entry = null;
    838                 if(!Utility.isIndex(index)) {
    839                      ///ystem.err.println("\tThis is an alias.");
    840                      // Store this for later, as its exactly the same entry we'd get had we found the last component of a proper index.
    841                      entry = (Entry) get(index);
    842                      index = entry.index;
    843                      ///ystem.err.println("\tIndex is actually: " + index);
    844                 }
     837        Entry entry = null;
     838        if(!Utility.isIndex(index)) {
     839        ///ystem.err.println("\tThis is an alias.");
     840        // Store this for later, as its exactly the same entry we'd get had we found the last component of a proper index.
     841        entry = (Entry) get(index);
     842        index = entry.index;
     843        ///ystem.err.println("\tIndex is actually: " + index);
     844        }
    845845                // Now build the hierarchy if necessary.
    846                 int dot_index = -1;
    847                 if((dot_index = index.indexOf(".")) != -1) {
    848                      ///ystem.err.println("\tHierarchy information required -->");
    849                      value.append(getValue(index.substring(0, dot_index)));
    850                      value.append("\\");
    851                      ///ystem.err.println("\t<-- Hierarchy information complete");
    852                 }
    853                 if(entry == null) {
    854                      entry = (Entry) get(index);
    855                 }
    856                 if(entry != null) {
    857                      value.append(entry.value);
    858                 }
    859                 entry = null;
     846        int dot_index = -1;
     847        if((dot_index = index.indexOf(".")) != -1) {
     848        ///ystem.err.println("\tHierarchy information required -->");
     849        value.append(getValue(index.substring(0, dot_index)));
     850        value.append("\\");
     851        ///ystem.err.println("\t<-- Hierarchy information complete");
     852        }
     853        if(entry == null) {
     854        entry = (Entry) get(index);
     855        }
     856        if(entry != null) {
     857        value.append(entry.value);
     858        }
     859        entry = null;
    860860                ///ystem.err.println("\tFinal value is: '" + value.toString() + "'\n");
    861                 return value.toString();
    862           }
    863 
    864           private class Entry {
    865                 public String alias = null;
    866                 public String index = null;
    867                 public String value = null;
    868                 public Entry(String index, String alias, String value) {
    869                      this.alias = alias;
    870                      this.index = index;
    871                      this.value = value;
    872                 }
    873           }
    874     }
    875 
    876     private class MetadataXMLFileSearch {
    877           public File file;
    878           public String filename;
    879           public MetadataXMLFileSearch(File file, String filename) {
    880                 this.file = file;
    881                 this.filename = filename;
    882           }
    883     }
     861        return value.toString();
     862    }
     863
     864    private class Entry {
     865        public String alias = null;
     866        public String index = null;
     867        public String value = null;
     868        public Entry(String index, String alias, String value) {
     869        this.alias = alias;
     870        this.index = index;
     871        this.value = value;
     872        }
     873    }
     874    }
     875
     876    private class MetadataXMLFileSearch {
     877    public File file;
     878    public String filename;
     879    public MetadataXMLFileSearch(File file, String filename) {
     880        this.file = file;
     881        this.filename = filename;
     882    }
     883    }
    884884}
Note: See TracChangeset for help on using the changeset viewer.