/** *######################################################################### * * A component of the Gatherer application, part of the Greenstone digital * library suite from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * *

* * Author: John Thompson, Greenstone Digital Library, University of Waikato * *

* * Copyright (C) 1999 New Zealand Digital Library Project * *

* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * *

* * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

* * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *######################################################################## */ package org.greenstone.gatherer.cdm; /************************************************************************************** * Title: Gatherer * Description: The Gatherer: a tool for gathering and enriching a digital collection. * Copyright: Copyright (c) 2001 * Company: The University of Waikato * Written: 02/05/02 * Revised: 17/11/02 - Commented **************************************************************************************/ import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.cdm.Subcollection; import org.greenstone.gatherer.cdm.SubIndex; import org.greenstone.gatherer.cdm.SubIndexes; import org.greenstone.gatherer.msm.ElementWrapper; import org.greenstone.gatherer.util.ExclusiveListSelectionListener; import org.w3c.dom.Element; /** This class maintains a list of subcollections within our collection, and also records which of these subcollections we are building indexes for. * @author John Thompson, Greenstone Digital Library, University of Waikato * @version 2.1 */ public class SubcollectionManager extends DefaultListModel { /** A reference to the manager so we can get access to languages. */ private CollectionDesignManager manager; /** The controls used to edit the settings of this manager. */ private Control controls = null; /** A reference to this class so we can use it as a model in our inner classes. */ private DefaultListModel model = null; /** The default index for the subcollections. */ private DefaultSubIndex default_index = null; /** A reference to the Gatherer. */ private Gatherer gatherer = null; /** A hashtable of subcollections, with mappings from name to subcollection. */ private Hashtable subcollections = null; /** A vector-type structure of subcollection indexes. */ private SubIndexes subindexes = null; /** A vector containing all of the subcollection commands that are unresolved, or in other words require that all 'subcollection ""' to have been previously parsed before we can be certain their references will make sense. */ private ArrayList unresolved = null; /** Constructor. * @param gatherer A reference to the Gatherer. * @see org.greenstone.gatherer.cdm.SubIndexes */ public SubcollectionManager(Gatherer gatherer, CollectionDesignManager manager) { super(); this.gatherer = gatherer; this.manager = manager; this.model = this; this.subcollections = new Hashtable(); this.subindexes = new SubIndexes(); this.unresolved = new ArrayList(); } /** Method to add a subindex. * @param subindex A SubIndex. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager */ public void addSubIndex(SubIndex subindex) { if(!subindexes.contains(subindex)) { subindexes.addElement(subindex); gatherer.c_man.configurationChanged(); } } /** Method to add a new subcollection. Adds it to both the underlying list model and the hashtable mapping names to subcollections. * @param sub The Subcollection to add. * @return A boolean indicating if the addition was successful (true) or not (false). * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager */ public boolean addSubcollection(Subcollection sub) { if(!contains(sub)) { addElement(sub); subcollections.put(sub.getName(), sub); gatherer.c_man.configurationChanged(); return true; } return false; } /** A method to add a new subcollection, by specifying its name, metadata element if applicable and Perl expression to filter pages into or out of this collection. * @param name A <>>String> which is a unique identifier of a subcollection. * @param include A boolean indicating whether this is an inclusion filter (true) or an exclusion one (false). * @param element An <>>Element> which is either the metadata whose value should be matched against the given expression, or null if you wish to match against the file name. * @param exp A <>>String> containing a Perl expression which is used as the filter for this subcollection. * @param flags A String listing any special flags to be applied when matching the expression. * @return A boolean with a value of true if the addition was successful. * @see org.greenstone.gatherer.cdm.Subcollection */ public boolean addSubcollection(String name, boolean include, String element, String exp, String flags) { Subcollection sub = null; if(element != null) { sub = new Subcollection(name, include, element, exp, flags); } else { sub = new Subcollection(name, include, exp, flags); } return addSubcollection(sub); } /** Refresh all derived components using this manager as a model. */ public void changed() { fireContentsChanged(this, 0, getSize() - 1); } /** Method to retrieve the controls for this manager. * @return A Control object which contains the controls used to edit the subcollection data. */ public Control getControls() { if(controls == null) { controls = new Control(); } return controls; } /** Method to retrieve the default index. * @return A DefaultSubIndex object. */ public DefaultSubIndex getDefaultSubIndex() { return default_index; } /** Method to retrieve a certain subcollection by its name. * @param name A String which is used as the key for finding the matching subcollection. * @return The requested Subcollection or null if no such subcollection exists. */ public Subcollection getSubcollection(String name) { return (Subcollection) subcollections.get(name); } /** Retrieve a certain subindex given its name. * @param name the String representation of a subindex. * @return the SubIndex requested or null if no such subindex. */ public SubIndex getSubIndex(String name) { for(int i = 0; i < subindexes.size(); i++) { SubIndex subindex = (SubIndex) subindexes.get(i); if(subindex.toString().equals(name)) { return subindex; } } return null; } /** Method to get all of the subindexes set. * @return A SubIndexes object containing all the defined indexes. */ public SubIndexes getSubIndexes() { return subindexes; } /** Method to get all of the subcollections defined. * @return A Vector of subcollections. */ public Vector getSubcollections() { Vector subcollections = new Vector(); for(int i = 0; i < size(); i++) { subcollections.add(get(i)); } return subcollections; } /** Mark the current controls, if any, as invalid and deallocate them. Any further use of the controls will requires them being rebuilt. * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control */ public void invalidateControls() { if(controls != null) { controls.destroy(); controls = null; } } /** This method attempts to parse a subcollection related command from the given command string. If such a string is successfully parsed, it is immediately added to the data within this collection. * @param command The String to be parsed. * @param finished This boolean is usually false when called, unless this parse call is being made -after- the entire collection configuration file has been read in in which case it is true. * @return A boolean which is true if a command was parsed, false otherwise. * @see org.greenstone.gatherer.cdm.CommandTokenizer * @see org.greenstone.gatherer.cdm.DefaultSubIndex * @see org.greenstone.gatherer.cdm.Subcollection * @see org.greenstone.gatherer.cdm.SubIndex */ public boolean parse(String command, boolean finished) { String temp = command.toLowerCase(); CommandTokenizer tokenizer = new CommandTokenizer(command); tokenizer.nextToken(); // Throw away head. if(temp.startsWith("subcollection")) { if(tokenizer.countTokens() >= 2) { String name = tokenizer.nextToken(); String pattern = tokenizer.nextToken(); addSubcollection(new Subcollection(name, pattern)); return true; } } else if(temp.startsWith("indexsubcollections")) { // These entries depend on what subcollections have previously been set, so we cannot safely parse them until after the file is complete. if(!finished) { unresolved.add(command); } else { while(tokenizer.hasMoreTokens()) { addSubIndex(new SubIndex(tokenizer.nextToken(), this)); } } return true; } else if(temp.startsWith("defaultsubcollection")) { // These entries depend on what subcollections have previously been set, so we cannot safely parse them until after the file is complete. if(!finished) { unresolved.add(command); } else { if(tokenizer.hasMoreTokens()) { setDefaultSubIndex(new DefaultSubIndex(tokenizer.nextToken(), this)); } } return true; } return false; } /** Method to remove a certain subindex. * @param subindex The SubIndex you wish to remove. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager */ public void removeSubIndex(SubIndex subindex) { subindexes.removeElement(subindex); gatherer.c_man.configurationChanged(); } /** Method to remove all of the subindexes that contain a certain subcollection. * @param sub The Subcollection that you wish to remove. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.cdm.SubIndex * @see org.greenstone.gatherer.collection.CollectionManager */ public void removeSubIndexes(Subcollection sub) { for(int i = subindexes.size() - 1; i >= 0; i--) { SubIndex subindex = (SubIndex)subindexes.get(i); if(subindex.containsSubcollection(sub.getName())) { subindexes.removeElement(subindex); } } gatherer.c_man.configurationChanged(); } /** Method to remove the given subcollection. * @param sub The Subcollection you want to remove. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager */ public void removeSubcollection(Subcollection sub) { removeElement(sub); subcollections.remove(sub); gatherer.c_man.configurationChanged(); } /** Method to retry the parsing of commands that were previously unable to be parsed as they referenced subcollections that may not have been instantiated. */ public void reparseUnresolved() { for(int i = 0; i < unresolved.size(); i++) { parse((String)unresolved.get(i), true); } unresolved.clear(); } /** Method to set the default subcollection index. * @param subcollection The Subcollection to use as the default index. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager */ public void setDefaultSubIndex(DefaultSubIndex subindex) { this.default_index = subindex; if(subindex != null) { addSubIndex(subindex.getSubIndex()); } gatherer.c_man.configurationChanged(); } /** This method causes the contents of this manager to be converted to a string, which is accomplished by calling toString() on each subcollection, then printing out the contents of the index vector. * @return A String containing a block of configuration commands. * @see org.greenstone.gatherer.cdm.SubIndex * @see org.greenstone.gatherer.cdm.Subcollection */ public String toString() { String text = ""; // Retrieve the subcollection names and sort them. if(subcollections.size() > 0) { Vector names = new Vector(subcollections.keySet()); Collections.sort(names); for(int i = 0; i < names.size(); i++) { Subcollection sub = (Subcollection) subcollections.get(names.get(i)); text = text + sub.toString(); } // Now add a entry, separated by spaces for each subcollection // index. if(subindexes.size() > 0) { text = text + subindexes.toString(); } // Finally add the default subcollection index if necessary. if(default_index != null) { text = text + default_index.toString(); } text = text + "\n"; } // Otherwise if there were no subcollections, there aren't going to be // subcollection indexes, nor a default subcollection index are there. return text; } /** Method to retrieve a phrase from the dictionary based on a key. * @param key A String used to find the correct phrase. * @param args A String[] of arguments used in formatting and filling out the phrase. * @return A String containing the correct phrase with the correct formatting. */ private String get(String key) { return get(key, null); } /** Method to retrieve a phrase from the dictionary based on a key. * @param key A String used to find the correct phrase. * @return A String containing the correct phrase with the correct formatting. * @see org.greenstone.gatherer.Dictionary * @see org.greenstone.gatherer.Gatherer */ private String get(String key, String args[]) { if(key.indexOf(".") == -1) { key = "CDM.SubcollectionManager." + key; } return gatherer.dictionary.get(key, args); } /** This class creates a JPanel containing serveral more controls used for editing subcollection information. */ private class Control extends JPanel { /** true if the current selection has changed. */ private boolean changed = false; /** Button to add a subcollection. */ private JButton add = null; /** Button to add a subindex. */ private JButton add_index = null; /** Button to clear the default subindex. */ private JButton clear_default = null; /** Button to remove a subcollection. */ private JButton remove = null; /** Button to remove a subindex. */ private JButton remove_index = null; /** Button to set the default subindex. */ private JButton set_default = null; /** Button to cause an update of the subcollections. */ private JButton update = null; /** A combobox allowing you to choose which field the data for matching should be taken from. Includes text body and filename, as well as all assigned metadata elements. */ private JComboBox source = null; /** The list of assigned subcollections. */ private JList subcollection_list = null; /** The list of subcollections available for the creation of subindexes. */ private JList subcollection_list_2 = null; /** The list of assigned subindexes. */ private JList subindexes_list = null; /** The label denoting the list of subindexes. */ private JLabel subindexes_label = null; /** The panel containing the subcollection controls. */ private JPanel subcollection_pane = null; /** The panel containing the subindex controls. */ private JPanel subindex_pane = null; /** The tabbed pane used to store the subcollection and subindex controls. */ private JTabbedPane tabbed_pane = null; /** The area used to display inline instructions. */ private JTextArea instructions = null; /** The field displaying the name of the default subindex. */ private JTextField default_value = null; /** A field used for specifying flags for the PERL expression matching, such as 'i' for case insensitive. */ private JTextField flags = null; /** The pattern the source text must match. */ private JTextField match = null; /** The name of this subcollection. */ private JTextField name = null; /** The name of this subindex. */ private JTextField subindex_name; /** When this button is selected the filter matching files are excluded. */ private JToggleButton exclude = null; /** When this button is selected the filter matching files are included. */ private JToggleButton include = null; /** The existing subcollection whose details you are reviewing, if any. */ private Subcollection current = null; /** Constructor, creates the outer parts of the view, then calls two methods in turn to create the subcollection controls and subindex controls. */ public Control() { // Create JPanel border_pane = new JPanel(); JPanel header_pane = new JPanel(); instructions = new JTextArea(get("Instructions")); instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); instructions.setEditable(false); instructions.setLineWrap(true); instructions.setRows(5); instructions.setWrapStyleWord(true); tabbed_pane = new JTabbedPane(); JLabel title = new JLabel(get("Title")); title.setHorizontalAlignment(JLabel.CENTER); createSubCollection(); createSubIndex(); // Add listeners // Layout instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); header_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); header_pane.setLayout(new BorderLayout()); header_pane.add(title, BorderLayout.NORTH); header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); tabbed_pane.addTab(get("Subcollection_Controls"), subcollection_pane); tabbed_pane.addTab(get("Subindex_Controls"), subindex_pane); tabbed_pane.addTab(get("Language_Controls"), manager.languages.getControls()); border_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); border_pane.setLayout(new BorderLayout()); border_pane.add(tabbed_pane, BorderLayout.CENTER); setLayout(new BorderLayout()); add(header_pane, BorderLayout.NORTH); add(border_pane, BorderLayout.CENTER); } /** Create the subcollection controls. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control.AddListener * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control.ListListener * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control.RemoveListener * @see org.greenstone.gatherer.collection.CollectionManager * @see org.greenstone.gatherer.msm.MetadataSetManager */ public void createSubCollection() { // Create add = new JButton(get("Add")); add.setMnemonic(KeyEvent.VK_A); add.setEnabled(false); JPanel button_pane = new JPanel(); JPanel button_pane_1 = new JPanel(); JPanel button_pane_3 = new JPanel(); exclude = new JToggleButton(get("Exclude")); exclude.setMnemonic(KeyEvent.VK_X); flags = new JTextField(); JLabel flags_label = new JLabel(get("Flags")); include = new JToggleButton(get("Include")); include.setMnemonic(KeyEvent.VK_I); JLabel inclusive_label = new JLabel(get("Inclusive")); JPanel inclusive_pane = new JPanel(); match = new JTextField(); JLabel match_label = new JLabel(get("Match")); name = new JTextField(); JLabel name_label = new JLabel(get("Name")); remove = new JButton(get("Remove")); remove.setMnemonic(KeyEvent.VK_R); remove.setEnabled(false); Vector source_model = gatherer.c_man.msm.getAssignedElements(); source_model.add(0, "Filename"); source = new JComboBox(source_model); JLabel source_label = new JLabel(get("Source")); subcollection_list = new JList(model); subcollection_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); subcollection_pane = new JPanel(); ButtonGroup bg = new ButtonGroup(); bg.add(include); bg.add(exclude); include.setSelected(true); JPanel subcollection_list_pane = new JPanel(); JLabel subcollection_list_label = new JLabel(get("Assigned")); // Add listeners SubCollectionChangeListener cl = new SubCollectionChangeListener(); add.addActionListener(new AddSubCollectionListener()); remove.addActionListener(new RemoveSubCollectionListener()); subcollection_list.addListSelectionListener(new SubCollectionListListener()); //exclude.addActionListener(cl); //include.addActionListener(cl); //source.addActionListener(cl); //flags.addDocumentListener(cl); match.getDocument().addDocumentListener(cl); name.getDocument().addDocumentListener(cl); // Layout inclusive_pane.setLayout(new GridLayout()); inclusive_pane.add(include); inclusive_pane.add(exclude); button_pane_1.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); button_pane_1.setLayout(new GridLayout(5, 2)); button_pane_1.add(name_label); button_pane_1.add(name); button_pane_1.add(source_label); button_pane_1.add(source); button_pane_1.add(match_label); button_pane_1.add(match); button_pane_1.add(inclusive_label); button_pane_1.add(inclusive_pane); button_pane_1.add(flags_label); button_pane_1.add(flags); button_pane_3.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); button_pane_3.setLayout(new GridLayout(1,2)); button_pane_3.add(add); button_pane_3.add(remove); button_pane.setLayout(new BorderLayout()); button_pane.add(button_pane_1, BorderLayout.CENTER); button_pane.add(button_pane_3, BorderLayout.SOUTH); subcollection_list_pane.setLayout(new BorderLayout()); subcollection_list_pane.add(subcollection_list_label, BorderLayout.NORTH); subcollection_list_pane.add(new JScrollPane(subcollection_list), BorderLayout.CENTER); subcollection_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); subcollection_pane.setLayout(new BorderLayout()); subcollection_pane.add(subcollection_list_pane, BorderLayout.CENTER); subcollection_pane.add(button_pane, BorderLayout.SOUTH); } /** Create the subindex controls. * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control.AddSubIndexListener * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control.ClearDefaultListener * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control.ChangeListener * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control.RemoveSubIndexListener * @see org.greenstone.gatherer.cdm.SubcollectionManager.Control.SetDefaultListener * @see org.greenstone.gatherer.util.ExclusiveListListener */ public void createSubIndex() { // Creation JPanel subindex_name_panel = new JPanel(); JLabel subindex_name_label = new JLabel(get("PartitionName")); subindex_name = new JTextField(); add_index = new JButton(get("Add_Subindex")); add_index.setMnemonic(KeyEvent.VK_A); add_index.setEnabled(false); JPanel button_pane_2 = new JPanel(); clear_default = new JButton(get("Clear_Default_Subindex")); clear_default.setMnemonic(KeyEvent.VK_C); if(default_index == null) { clear_default.setEnabled(false); } JLabel default_label = new JLabel(get("Default_Subindex")); JPanel default_pane = new JPanel(); if(default_index == null) { default_value = new JTextField(); } else { default_value = new JTextField(default_index.getSubIndex().toString()); } default_value.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); default_value.setEditable(false); JPanel subindex_inner_pane_1 = new JPanel(); JPanel subindex_inner_pane_2 = new JPanel(); remove_index = new JButton(get("Remove_Subindex")); remove_index.setMnemonic(KeyEvent.VK_R); set_default = new JButton(get("Set_Default_Subindex")); set_default.setMnemonic(KeyEvent.VK_S); JLabel subcollection_label = new JLabel(get("Subcollection")); subcollection_list_2 = new JList(model); JPanel list_2_pane = new JPanel(); subindex_pane = new JPanel(); JLabel subindexes_label = new JLabel(get("Subindexes")); subindexes_list = new JList(subindexes); subindexes_list.setCellRenderer(new SubIndexListCellRenderer()); subindexes_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); JPanel subindexes_pane = new JPanel(); // Add listeners ExclusiveListSelectionListener ell = new ExclusiveListSelectionListener(); ell.add(subcollection_list_2); ell.add(subindexes_list); add_index.addActionListener(new AddSubIndexListener()); clear_default.addActionListener(new ClearDefaultSubIndexListener()); remove_index.addActionListener(new RemoveSubIndexListener()); set_default.addActionListener(new SetDefaultSubIndexListener()); subindex_name.getDocument().addDocumentListener(new SubIndexNameDocumentListener()); subcollection_list_2.addListSelectionListener(new SubCollectionList2Listener()); // Layout subindex_name_panel.setLayout(new BorderLayout()); subindex_name_panel.add(subindex_name_label, BorderLayout.WEST); subindex_name_panel.add(subindex_name, BorderLayout.CENTER); list_2_pane.setLayout(new BorderLayout()); list_2_pane.add(subcollection_label, BorderLayout.NORTH); list_2_pane.add(new JScrollPane(subcollection_list_2), BorderLayout.CENTER); subindexes_pane.setLayout(new BorderLayout()); subindexes_pane.add(subindexes_label, BorderLayout.NORTH); subindexes_pane.add(new JScrollPane(subindexes_list), BorderLayout.CENTER); subindex_inner_pane_2.setLayout(new GridLayout(1,2,0,5)); subindex_inner_pane_2.add(list_2_pane); subindex_inner_pane_2.add(subindexes_pane); default_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); default_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,5,5,5), BorderFactory.createRaisedBevelBorder()), BorderFactory.createEmptyBorder(2,2,2,2))); default_pane.setLayout(new BorderLayout()); default_pane.add(default_label, BorderLayout.WEST); default_pane.add(default_value, BorderLayout.CENTER); subindex_inner_pane_1.setLayout(new BorderLayout()); subindex_inner_pane_1.add(subindex_name_panel, BorderLayout.NORTH); subindex_inner_pane_1.add(subindex_inner_pane_2, BorderLayout.CENTER); subindex_inner_pane_1.add(default_pane, BorderLayout.SOUTH); button_pane_2.setLayout(new GridLayout(2,2)); button_pane_2.add(add_index); button_pane_2.add(remove_index); button_pane_2.add(clear_default); button_pane_2.add(set_default); subindex_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); subindex_pane.setLayout(new BorderLayout()); subindex_pane.add(subindex_inner_pane_1, BorderLayout.CENTER); subindex_pane.add(button_pane_2, BorderLayout.SOUTH); } /** Method to unregister any listeners to avoid memory leaks. */ public void destroy() { } /** We have overriden this method to provide completely different functionality, given that our JPanel will never actually have focus. We call this method in GUI on the view we are about to replace, and this method checks if a change has occured and if so updates the current object. * @return A boolean which is always false. * @see org.greenstone.gatherer.msm.ElementWrapper */ public boolean hasFocus() { // If we have a current metadata open, and something has changed then save the change. if(changed && current != null) { String n = name.getText(); String s = null; Object o = source.getSelectedItem(); if(o instanceof ElementWrapper) { ElementWrapper e = (ElementWrapper)o; s = e.toString(); } String e = match.getText(); String f = flags.getText(); if(n != null && n.length() > 0 && (s == null || s.length() > 0) && e != null && e.length() > 0) { current.update(n, s, e, include.isSelected(), f); int index = model.indexOf(current); changed(); subindexes.changed(); if(default_index != null) { default_value.setText(default_index.getSubIndex().toString()); } } changed = false; } return false; } /** Overriden to ensure the instructions are scrolled to top. */ public void updateUI() { if(instructions != null) { instructions.setCaretPosition(0); } super.updateUI(); } /** Method to validate the current subcollection editor values, and enable or disable controls (add button) based on said values. * we want to disable add until a new name is present */ protected void validateAdd() { if(changed && name.getText().length() > 0 && match.getText().length() > 0) { if (subcollections.containsKey(name.getText())) { add.setEnabled(false); } else { add.setEnabled(true); } } else { add.setEnabled(false); } } /** Listens for actions apon the 'add' button in the SubcollectionManager controls, and if detected calls the addSubcollection method of the manager with a newly created subcollection. */ private class AddSubCollectionListener implements ActionListener { /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we wish to retrieve information from the various edit controls, and if we have sufficient data to build a new subcollection do so. * @param event An ActionEvent containing information about the event. * @see org.greenstone.gatherer.cdm.Subcollection * @see org.greenstone.gatherer.msm.ElementWrapper */ public void actionPerformed(ActionEvent event) { String n = name.getText(); // not allowed spaces! if (n.indexOf(' ')!=-1) { n = n.substring(0, n.indexOf(' ')); name.setText(n); } String s = null; Object o = source.getSelectedItem(); if(o instanceof ElementWrapper) { ElementWrapper e = (ElementWrapper)o; s = e.toString(); } String e = match.getText(); String f = flags.getText(); if(n != null && n.length() > 0 && (s == null || s.length() > 0) && e != null && e.length() > 0) { Subcollection sub = new Subcollection(n, include.isSelected(), s, e, f); addSubcollection(sub); } changed = false; validateAdd(); } } /** Listens for actions apon the 'add subindex' button in the SubcollectionManager controls, and if detected calls the addSubindex method of the manager with a newly created subindex. */ private class AddSubIndexListener implements ActionListener { /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to check if they have a series of subcollections selected, and if so build an new subindex based apon them. * @param event An ActionEvent containing information about the event. * @see org.greenstone.gatherer.cdm.SubIndex */ public void actionPerformed(ActionEvent event) { if(!subcollection_list_2.isSelectionEmpty() && subindex_name.getText().length() > 0) { Vector selected = new Vector(); Object raw[] = subcollection_list_2.getSelectedValues(); for(int i = 0; i < raw.length; i++) { selected.add(raw[i]); } SubIndex subindex = new SubIndex(selected); addSubIndex(subindex); // Add the subindexes name. CollectionMeta metadata = new CollectionMeta(manager, subindex, manager.languages.getDefaultLanguage(), subindex_name.getText()); manager.collectionmetadatum.addMetadata(metadata); } } } /** This class listens for any key entry in a text field, selection change in a combobox or button click, and when detected sets the changed flag to true. Its also convenient to use this class to test if the add button should be active yet. */ private class SubCollectionChangeListener implements DocumentListener, ActionListener { /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to record that somethings changed, then validate the controls. * @param event An ActionEvent containing information about the event. */ public void actionPerformed(ActionEvent event) { changed = true; validateAdd(); } public void changedUpdate(DocumentEvent event) { changed = true; validateAdd(); } public void insertUpdate(DocumentEvent event) { changed = true; validateAdd(); } public void removeUpdate(DocumentEvent event) { changed = true; validateAdd(); } } /** Listens for actions apon the 'clear default subindex' button in the SubcollectionManager controls, and if detected calls the setDefaultSubIndex() method of the manager with null. */ private class ClearDefaultSubIndexListener implements ActionListener { /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to clear the default subindex. * @param event An ActionEvent containing information about the event. */ public void actionPerformed(ActionEvent event) { setDefaultSubIndex(null); clear_default.setEnabled(false); default_value.setText(""); } } /** Listens for actions apon the 'remove' button in the SubcollectionManager controls, and if detected calls the remove method of the manager with the SubIndex selected for removal. */ private class RemoveSubCollectionListener implements ActionListener { /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to check if they have a subcolleciton selected, and if so remove both it and any subindexes based on it. * @param event An ActionEvent containing information about the event. * @see org.greenstone.gatherer.cdm.Subcollection */ public void actionPerformed(ActionEvent event) { if(!subcollection_list.isSelectionEmpty()) { Subcollection sub_col = (Subcollection)subcollection_list.getSelectedValue(); removeSubIndexes(sub_col); removeSubcollection(sub_col); } } } /** Listens for actions apon the 'remove subindex' button in the SubcollectionManager controls, and if detected calls the removeSubIndex method of the manager with the SubIndex selected for removal. */ private class RemoveSubIndexListener implements ActionListener { /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to check if they have a subindex selected, and if so remove it. * @param event An ActionEvent containing information about the event. * @see org.greenstone.gatherer.cdm.SubIndex */ public void actionPerformed(ActionEvent event) { if(!subindexes_list.isSelectionEmpty()) { removeSubIndex((SubIndex)subindexes_list.getSelectedValue()); } } } /** Listens for actions apon the 'set default subindex' button in the SubcollectionManager controls, and if detected calls the setDefaultSubIndex method of the manager with the SubIndex selected for default. */ private class SetDefaultSubIndexListener implements ActionListener { /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we want to check if they have a subindex selected, and if so set it as default. * @param event An ActionEvent containing information about the event. * @see org.greenstone.gatherer.cdm.DefaultSubIndex * @see org.greenstone.gatherer.cdm.SubIndex */ public void actionPerformed(ActionEvent event) { if(!subindexes_list.isSelectionEmpty()) { setDefaultSubIndex(new DefaultSubIndex((SubIndex)subindexes_list.getSelectedValue())); clear_default.setEnabled(true); default_value.setText(default_index.getSubIndex().toString()); } } } /** This class listens for selections in the list on the subcollections pane of the SubcollectionManager, and updates the controls as necessary to reflect selection. */ private class SubCollectionListListener implements ListSelectionListener { /** Any implementation of ListSelectionListener must include this method so we can be informed when the selection changes. In this case we want to execute any changes the users made to the entry, then update the controls with details of the new selection. * @param event A ListSelectionEvent containing information related to this event. * @see org.greenstone.gatherer.cdm.Subcollection * @see org.greenstone.gatherer.msm.ElementWrapper */ public void valueChanged(ListSelectionEvent event) { // If we have a previous collection and the users changed something, but not added, then update subcollection. if(changed && current != null) { String n = name.getText(); String s = null; Object o = source.getSelectedItem(); if(o instanceof ElementWrapper) { ElementWrapper e = (ElementWrapper)o; s = e.toString(); } String e = match.getText(); String f = flags.getText(); if(n != null && n.length() > 0 && (s == null || s.length() > 0) && e != null && e.length() > 0) { current.update(n, s, e, include.isSelected(), f); int index = model.indexOf(current); changed(); subindexes.changed(); if(default_index != null) { default_value.setText(default_index.getSubIndex().toString()); } } } // Now load the new entry. if(!subcollection_list.isSelectionEmpty()) { current = (Subcollection) subcollection_list.getSelectedValue(); flags.setText(current.getFlags()); include.setSelected(current.getInclude()); exclude.setSelected(!current.getInclude()); match.setText(current.getExpression()); name.setText(current.getName()); String s = current.getSource(); int pos = 0; Object value = source.getItemAt(pos); while(value != null) { if(value instanceof ElementWrapper) { ElementWrapper e = (ElementWrapper) value; if(e.toString().equals(s)) { source.setSelectedIndex(pos); value = null; } else { pos++; value = source.getItemAt(pos); } } else if(value.toString().equals(s)) { source.setSelectedIndex(pos); value = null; } else { pos++; value = source.getItemAt(pos); } } // Can't add one thats already there. add.setEnabled(false); // You can remove it though... remove.setEnabled(true); } else { flags.setText(""); include.setSelected(true); match.setText(""); name.setText(""); source.setSelectedIndex(0); remove.setEnabled(false); } // Have to do this after so we don't get called when nothings actually changed. changed = false; } } private class SubCollectionList2Listener implements ListSelectionListener { public void valueChanged(ListSelectionEvent event) { if(!event.getValueIsAdjusting()) { add_index.setEnabled(!subcollection_list_2.isSelectionEmpty() && subindex_name.getText().length() > 0); } } } private class SubIndexListCellRenderer extends DefaultListCellRenderer { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { StringBuffer text = new StringBuffer(value.toString()); // Retrieve the indexes name if any. CollectionMeta metadata = manager.collectionmetadatum.getMetadata(value, manager.languages.getDefaultLanguage(), true); if(metadata != null) { text.append(" \""); text.append(metadata.getValue()); text.append("\""); } return super.getListCellRendererComponent(list, text.toString(), index, isSelected, cellHasFocus); } } private class SubIndexNameDocumentListener implements DocumentListener { /** Gives notification that an attribute or set of attributes changed. */ public void changedUpdate(DocumentEvent e) { update(); } /** Gives notification that there was an insert into the document. */ public void insertUpdate(DocumentEvent e) { update(); } /** Gives notification that a portion of the document has been removed. */ public void removeUpdate(DocumentEvent e) { update(); } /** The text area has changed in some way. Given that this can only happed when we are editing or adding a text fragment we better respond appropriately. */ private void update() { add_index.setEnabled(!subcollection_list_2.isSelectionEmpty() && subindex_name.getText().length() > 0); } } } }