/** *######################################################################### * * 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; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.StringReader; import java.lang.Exception; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.jar.JarFile; import javax.swing.BorderFactory; import javax.swing.DefaultListCellRenderer; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.apache.xerces.parsers.DOMParser; import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.cdm.Argument; import org.greenstone.gatherer.cdm.CollectionDesignManager; import org.greenstone.gatherer.cdm.CommandTokenizer; import org.greenstone.gatherer.cdm.Classifier; import org.greenstone.gatherer.cdm.CustomClassifier; import org.greenstone.gatherer.cdm.DynamicListModel; import org.greenstone.gatherer.file.FileNode; import org.greenstone.gatherer.msm.MSMEvent; import org.greenstone.gatherer.msm.MSMListener; import org.greenstone.gatherer.msm.MSMUtils; import org.greenstone.gatherer.util.Utility; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.InputSource; /** This class is responsible for keeping track of all the classifiers assigned to this collection, and providing methods for adding and removing them. * @author John Thompson, Greenstone Digital Library, University of Waikato * @version 2.3 */ // #################################################################################### // Optimization Saving // #################################################################################### // Vector -> ArrayList + Memory, + Processor, (pos. - Processor) // Unnecessary global references + Memory (5Kb+) // #################################################################################### public class ClassifierManager implements MSMListener { /** An interface to the Gatherer, the creator of this cdm module, for access to the Greenstone installation directory. */ private Gatherer gatherer = null; /** A reference to the CollectionDesignManager for access to other configuration managers. */ private CollectionDesignManager manager = null; /** The controls for editing the contents of this manager. */ private Control controls = null; /** A list of assigned classifiers. */ private DynamicListModel assigned = null; /** A list of known, but currently unassigned, classifiers. */ private DynamicListModel reserve = null; /** We may have somehow recieved a classifier command that are, in fact, custom classifiers which can refer to classifiers that haven't been parsed yet, so this holds a list of failed commands which are retried after the loading is complete. */ private ArrayList unresolved_commands = null; /** Constructor. * @param gatherer A reference to the Gatherer for access to the Dictionary. * @param manager A reference to the CollectionDesignManager itself. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.cdm.DynamicListModel * @see org.greenstone.gatherer.collection.CollectionManager * @see org.greenstone.gatherer.msm.MetadataSetManager * @see org.greenstone.gatherer.msm.MSMListener */ public ClassifierManager(Gatherer gatherer, CollectionDesignManager manager) { this.assigned = new DynamicListModel(); this.gatherer = gatherer; this.manager = manager; this.unresolved_commands = new ArrayList(); loadClassifiers(); saveClassifiers(); // Register as a MSMListener. Gatherer.c_man.getCollection().msm.addMSMListener(this); } /** Method to add a new classifier to reserve. * @param classifier The new Classifier. * @see org.greenstone.gatherer.cdm.DynamicListModel */ public void addClassifier(Classifier classifier) { if(!reserve.contains(classifier)) { reserve.addElement(classifier); } } /** Method to assign a classifier. * @param classifier The reserve Classifier to assign. * @see org.greenstone.gatherer.cdm.DynamicListModel */ public void assignClassifier(Classifier classifier) { if(!assigned.contains(classifier)) { assigned.addElement(classifier); classifier.setManager(this); gatherer.c_man.configurationChanged(); } } /** Method to assign a classifier. * @param classifier The CustomClassifier to assign. * @see org.greenstone.gatherer.cdm.DynamicListModel */ public void assignClassifier(CustomClassifier classifier) { if(!assigned.contains(classifier)) { assigned.addElement(classifier); classifier.setManager(this); gatherer.c_man.configurationChanged(); } } /** Destructor. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.cdm.CollectionDesignManager * @see org.greenstone.gatherer.cdm.DynamicListModel */ public void destroy() { // Deregister as a listener if(gatherer.c_man != null && gatherer.c_man.msm != null) { gatherer.c_man.msm.removeMSMListener(this); } // Null globals assigned = null; controls = null; gatherer = null; manager = null; reserve = null; unresolved_commands = null; } /** Method to retrieve the classifier with the given index. * @param index The index of the desired classifier as an int. * @return The requested Classifier as an Object or null if no such classifier exists. * @see org.greenstone.gatherer.cdm.DynamicListModel */ public Object getClassifier(int index) { if(0 <= index && index < assigned.size()) { return assigned.get(index); } return null; } /** Method to retrieve the named classifier. * @param name The name of the desired classifier as a String. * @return The requested Classifier or null if no such classifier exists. * @see org.greenstone.gatherer.cdm.DynamicListModel */ public Classifier getClassifier(String name) { for(int i = 0; i < reserve.size(); i++) { Classifier classifier = (Classifier)reserve.get(i); if(classifier.getName().equals(name)) { return classifier; } } // No success. return null; } /** Method to retrieve the control for this manager. * @return A JPanel containing the controls. */ public JPanel getControls() { if(controls == null) { controls = new Control(); } return controls; } /** Called whenever a metadata element changes significantly. * @param event A MSMEvent choc' full of event informationy goodness. */ public void elementChanged(MSMEvent event) { // Don't really care, as the elements dealt with here are all live references so changes like identifier change will propagate immediately. } /** Method to find the index of the given classifier within the assigned classifiers. * @param classifier The Classifier whose index you wish to find. * @return The index of the classifier as an int, which has a value of -1 if the classifier was not found. * @see org.greenstone.gatherer.cdm.DynamicListModel */ public int indexOf(Classifier classifier) { for(int i = 0; i < assigned.size(); i++) { Object elem = assigned.get(i); if (elem instanceof Classifier) { Classifier sibling = (Classifier)elem; if(sibling.equals(classifier)) { return i; } } } return -1; } // these two methods assume that a custome classifier can never be the same as a classifier public int indexOf(CustomClassifier classifier) { for(int i = 0; i < assigned.size(); i++) { Object elem = assigned.get(i); if (elem instanceof CustomClassifier) { CustomClassifier sibling = (CustomClassifier) assigned.get(i); if(sibling.equals(classifier)) { return i; } } } return -1; } /** Method to invalidate controls after a significant change in the system state. */ public void invalidateControls() { if(controls != null) { controls.destroy(); } controls = null; } /** Method to load the details of a single plug-in. * @param classifier The classifier File you wish to load. */ public void loadClassifier(File classifier) { ///ystem.err.println("Attempting to parse " + classifier.toString()); Document document = null; long start; long end; // Run classinfo on this classifier, and then send the results for parsing. try { String args[] = null; if(Utility.isWindows()) { args = new String[4]; if(gatherer.config.perl_path != null) { args[0] = gatherer.config.perl_path; } else { args[0] = "Perl.exe"; } args[1] = gatherer.config.gsdl_path + "bin" + File.separator + "script" + File.separator + "classinfo.pl"; args[2] = "-xml"; args[3] = getClassifierName(classifier); } else { args = new String[3]; args[0] = "classinfo.pl"; args[1] = "-xml"; args[2] = getClassifierName(classifier); } // Create the process. Runtime runtime = Runtime.getRuntime(); Process process = runtime.exec(args); BufferedReader error_in = new BufferedReader(new InputStreamReader(process.getErrorStream())); String line = ""; StringBuffer xml = new StringBuffer(""); boolean xml_content = false; while((line = error_in.readLine()) != null) { if(xml_content) { xml.append(line); xml.append("\n"); } else if(line.trim().startsWith("String that may include classifier information. * @return true if a classifier command was parsed, false otherwise. * @see org.greenstone.gatherer.cdm.Argument * @see org.greenstone.gatherer.cdm.Classifier * @see org.greenstone.gatherer.cdm.CommandTokenizer */ public boolean parse(String command) { String command_lc = command.toLowerCase(); if(command_lc.startsWith("classify")) { CommandTokenizer tokenizer = new CommandTokenizer(command); if(tokenizer.countTokens() >= 2) { tokenizer.nextToken(); // Throw away 'classifier' String name = tokenizer.nextToken(); // Try to locate the classifier with this name. Classifier classifier = getClassifier(name); // And if successful start to parse the arguments. if(classifier != null) { // Take a copy. classifier = classifier.copy(); String key = null; while((key = tokenizer.nextToken()) != null) { // Try to retrieve a matching argument. Argument argument = classifier.getArgument(key); if(argument != null) { // Set as assigned. argument.setAssigned(true); // And if the argument is of a parameter type, parse a parameter. if(argument.getType() != Argument.FLAG && tokenizer.hasMoreTokens()) { String value = tokenizer.nextToken(); // special hack for metadata if (argument.getType() == Argument.METADATA) { value = value.replace(':', MSMUtils.NS_SEP); if (value.indexOf(MSMUtils.NS_SEP)==-1){ value = Utility.EXTRACTED_METADATA_NAMESPACE +MSMUtils.NS_SEP+value; } } // shoudl probably do the same for METADATUM argument.setValue(value); } } // Argument cannot be matched. else { String cur_key = key; String value = tokenizer.nextToken(); if(value.startsWith("-")) { key = value; value = null; } else { key = null; } String custom = classifier.getCustom(); if(custom == null) { if(value == null) { classifier.setCustom(cur_key); } else { classifier.setCustom(cur_key + " " + value); } } else { if(value == null) { classifier.setCustom(custom + " " + cur_key); } else { classifier.setCustom(custom + " " + cur_key + " " + value); } } } } assignClassifier(classifier); return true; } else { ///ystem.err.println("Unknown classifier"); } } } else if(command_lc.startsWith("customclassifier")) { unresolved_commands.add(command); return true; } return false; } /** This method removes an assigned classifier. I was tempted to call it unassign, but remove is more consistant. Note that there is no way to remove a classifier from the reserve. * @param classifier The Classifier or CustomClassifier, as an Object, to remove. * @see org.greenstone.gatherer.cdm.DynamicListModel */ public void removeClassifier(Object classifier) { assigned.removeElement(classifier); gatherer.c_man.configurationChanged(); } /** Method which attempts to reparse obvious classifier commands which previously referenced unresovable Classifiers. * @see org.greenstone.gatherer.cdm.Classifier * @see org.greenstone.gatherer.cdm.CommandTokenizer * @see org.greenstone.gatherer.cdm.CustomClassifier */ public void reparseUnresolved() { for(int i = 0; i < unresolved_commands.size(); i++) { String command = (String) unresolved_commands.get(i); CommandTokenizer tokenizer = new CommandTokenizer(command); if(tokenizer.countTokens() >= 6) { tokenizer.nextToken();// Lose customclassifier // Get class name. String class_name = tokenizer.nextToken(); // Parse arguments. String replaces = null; String separations = null; while(tokenizer.hasMoreTokens()) { String arg_name = tokenizer.nextToken(); if(arg_name.equalsIgnoreCase("-replaces")) { replaces = tokenizer.nextToken(); } else if (arg_name.equalsIgnoreCase("-separations")){ separations = tokenizer.nextToken(); } } try { replaces = replaces.substring(2); int index = Integer.parseInt(replaces); Classifier original = (Classifier)getClassifier(index); if(original != null) { Class custom_classifier_class = Class.forName("org.greenstone.gatherer.cdm.custom." + class_name); CustomClassifier custom_classifier = (CustomClassifier) custom_classifier_class.newInstance(); custom_classifier.setGatherer(gatherer); custom_classifier.recreate(original, separations); assigned.add(index, custom_classifier); assigned.removeElement(original); } else { ///ystem.err.println("Missing original."); } } catch (Exception error) { error.printStackTrace(); } } } // Regardless of if they work, clear the commands. unresolved_commands.clear(); } /** Method to cache the current contents of reserve (known classifiers) to file. * @see org.greenstone.gatherer.util.Utility */ public void saveClassifiers() { try { FileOutputStream file = new FileOutputStream(Utility.BASE_DIR + "classifiers.dat"); ObjectOutputStream out = new ObjectOutputStream(file); out.writeObject(reserve); out.close(); } catch (Exception error) { } } /** Called when a metadata set changed significantly. * @param event A MSMEvent containing information about the set change. */ public void setChanged(MSMEvent event) { // Again, we would only worry about this if we contained 'inanimate' references to elements or something, but our references are live, and controls are rebuilt everytime a pop-up is needed. } /** Method used to determine the number of classifiers that have been assigned. * @return An int which is the number of classifiers. */ public int size() { return assigned.size(); } /** Method to print out a block of classifier commands, much like you'd find in a collection configuration file. * @return A String containing a series of classifier commands separated by new lines. * @see org.greenstone.gatherer.cdm.Classifier * @see org.greenstone.gatherer.cdm.CustomClassifier */ public String toString() { StringBuffer text = new StringBuffer(); for(int i = 0; i < assigned.size(); i++) { Object object = assigned.get(i); if(object instanceof Classifier) { Classifier classifier = (Classifier) object; text.append(classifier.toStringConfig()); } else if(object instanceof CustomClassifier) { CustomClassifier classifier = (CustomClassifier) object; text.append(classifier.getCommand()); text.append("\n"); text.append(classifier.getCustomCommand(i)); } text.append("\n"); } text.append("\n"); return text.toString(); } /** Called when a significant change has occured to a value tree for a certain element, however we take no further action. * @param event A MSMEvent containing information relevant to the event. */ public void valueChanged(MSMEvent event) { } /** Retrieve a phrase from the dictionary based on a certain key. * @param key The search String. * @return The matching phrase from the Dictionary. */ private String get(String key) { return get(key, null); } /** Retrieve a phrase from the dictionary based on a certain key and certain arguments. * @param key The search String. * @param args A String[] used to complete and format the returned phrase. * @return The matching phrase from the Dictionary. * @see org.greenstone.gatherer.Dictionary * @see org.greenstone.gatherer.Gatherer */ private String get(String key, String arg) { String[] args = null; if(arg != null) { args = new String[1]; args[0] = arg; } if(key.indexOf(".") == -1) { key = "CDM.ClassifierManager." + key; } return gatherer.dictionary.get(key, args); } /** Method to extract just the classifiers name from a file object. * @param classifier The File which references a certain classifier. * @return A String containing just the classifiers name, without extension. */ private String getClassifierName(File classifier) { String name = classifier.getName(); if(name.indexOf(".") != -1) { name = name.substring(0, name.indexOf(".")); } return name; } /** Method to initially load information from the standard plug-ins within the gsdl Perl library. * @see org.greenstone.gatherer.cdm.DynamicListModel * @see org.greenstone.gatherer.util.Utility */ private void loadClassifiers() { // Attempt to restore the cached file. try { FileInputStream file = new FileInputStream(Utility.BASE_DIR + "classifiers.dat"); ObjectInputStream input = new ObjectInputStream(file); reserve = (DynamicListModel) input.readObject(); } catch (Exception error) { } if(reserve == null) { reserve = new DynamicListModel(); // Retrieve the gsdl home directory... String directory = gatherer.config.gsdl_path; directory = directory + "perllib" + File.separator + "classify" + File.separator; loadClassifiers(new File(directory)); } } /** Method to load plug-in information from a specified directory. Of course no plug-ins may be found at this location. * @param directory A File indicating the directory to be scanned for plug-ins. * @see org.greenstone.gatherer.cdm.ParsingProgress */ private void loadClassifiers(File directory) { File files[] = directory.listFiles(); if(files != null) { // Create a progress indicator. ParsingProgress progress = new ParsingProgress(get("CDM.ClassifierManager.Parsing.Title"), get("CDM.ClassifierManager.Parsing.Message"), files.length); for(int i = 0; i < files.length; i++) { // We only want to check Perl Modules. if(files[i].getName().endsWith(".pm")) { loadClassifier(files[i]); } progress.inc(); } progress.dispose(); progress.destroy(); progress = null; } } /** Parses a DOM tree model turning it into a Classifier and its associated arguments. * @param root The Node at the root of the DOM model. * @return A newly created Classifier based on the information parsed from the DOM model. * @see org.greenstone.gatherer.cdm.Argument */ private Classifier parse(Node root) { Classifier classifier = new Classifier(); String node_name = null; for(Node node = root.getFirstChild(); node != null; node = node.getNextSibling()) { node_name = node.getNodeName(); if(node_name.equals("Name")) { String name = MSMUtils.getValue(node); // We can save ourselves some processing time if a classifier with this name already exists in our manager. If so retrieve it and return it. Classifier existing = getClassifier(name); if(existing != null) { return existing; } classifier.setName(name); } else if(node_name.equals("Desc")) { classifier.setDesc(MSMUtils.getValue(node)); } // Parse the multitude of arguments. else if(node_name.equals("Arguments")) { for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) { node_name = arg.getNodeName(); // An option. if(node_name.equals("Option")) { Argument argument = new Argument(); // If its an option we parse the multitude of details an options might have. for(Node det = arg.getFirstChild(); det != null; det = det.getNextSibling()) { node_name = det.getNodeName(); if(node_name.equals("Name")) { argument.setName(MSMUtils.getValue(det)); } else if(node_name.equals("Desc")) { argument.setDesc(MSMUtils.getValue(det)); } else if(node_name.equals("Type")) { argument.setType(MSMUtils.getValue(det)); } else if(node_name.equals("Default")) { argument.setDefault(MSMUtils.getValue(det)); } else if(node_name.equals("List")) { // Two final loops are required to parse lists. for(Node value = det.getFirstChild(); value != null; value = value.getNextSibling()) { if(value.getNodeName().equals("Value")) { String key = null; String desc = ""; for(Node subvalue = value.getFirstChild(); subvalue != null; subvalue = subvalue.getNextSibling()) { node_name = subvalue.getNodeName(); if(node_name.equals("Name")) { key = MSMUtils.getValue(subvalue); } else if(node_name.equals("Desc")) { desc = MSMUtils.getValue(subvalue); } } if(key != null) { argument.addOption(key, desc); } } } } else if(node_name.equals("Required")) { String v = MSMUtils.getValue(det); ///ystem.err.println("Required = " + v); if(v.equalsIgnoreCase("yes")) { ///ystem.err.println("Setting required to true."); argument.setRequired(true); } } } classifier.addArgument(argument); } // A super classifier class. else if(node_name.equals("ClasInfo")) { Classifier super_classifier = parse(arg); classifier.setSuper(super_classifier); } } } } if(classifier.getName() != null) { addClassifier(classifier); return classifier; } return null; } /** A class which provides controls for assigned and editing classifiers. */ private class Control extends JPanel { /** Button for adding classifiers. */ private JButton add = null; /** Button for configuring the selected classifier. */ private JButton configure = null; /** Button to remove the selected classifier. */ private JButton remove = null; /** A combobox containing all of the known classifiers, including those that may have already been assigned. */ private JComboBox classifier = null; /** A list of assigned classifiers. */ private JList classifier_list = null; /** The text area containing instructions on the use of this control. */ private JTextArea instructions = null; /** Constructor. * @see org.greenstone.gatherer.cdm.ClassifierManager.Control.AddListener * @see org.greenstone.gatherer.cdm.ClassifierManager.Control.ConfigureListener * @see org.greenstone.gatherer.cdm.ClassifierManager.Control.RemoveListener */ public Control() { Object classifiers[] = reserve.toArray(); ArrayList classifier_model = new ArrayList(); for(int i = 0; i < classifiers.length; i++) { classifier_model.add(((Classifier)classifiers[i]).getName()); } // Now we add custom classifiers. addCustomClassifiers(classifier_model); Collections.sort(classifier_model); // Create add = new JButton(get("Add")); JPanel button_pane = new JPanel(); JPanel central_pane = new JPanel(); configure = new JButton(get("Configure")); 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); classifier = new JComboBox(classifier_model.toArray()); classifier.setEditable(false); JLabel classifier_label = new JLabel(get("Classifier")); classifier_list = new JList(assigned); JLabel classifier_list_label = new JLabel(get("Assigned")); classifier_list_label.setHorizontalAlignment(JLabel.CENTER); classifier_list_label.setOpaque(true); JPanel classifier_list_pane = new JPanel(); JPanel classifier_pane = new JPanel(); remove = new JButton(get("Remove")); JLabel title = new JLabel(get("Title")); title.setHorizontalAlignment(JLabel.CENTER); title.setOpaque(true); JPanel temp = new JPanel(new BorderLayout()); // Listeners add.addActionListener(new AddListener()); configure.addActionListener(new ConfigureListener()); remove.addActionListener(new RemoveListener()); classifier_list.addMouseListener(new ClickListener()); // Layout title.setBorder(BorderFactory.createEmptyBorder(0,0,2,0)); instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); header_pane.setLayout(new BorderLayout()); header_pane.add(title, BorderLayout.NORTH); header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); classifier_list_label.setBorder(BorderFactory.createEmptyBorder(0,2,0,2)); classifier_list_pane.setLayout(new BorderLayout()); classifier_list_pane.add(classifier_list_label, BorderLayout.NORTH); classifier_list_pane.add(new JScrollPane(classifier_list), BorderLayout.CENTER); classifier_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); classifier_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); classifier_pane.setLayout(new GridLayout(1,2)); classifier_pane.add(classifier_label); classifier_pane.add(classifier); button_pane.setLayout(new GridLayout(3,1)); button_pane.add(add); button_pane.add(configure); button_pane.add(remove); // Scope these mad bordering skillz. temp.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,0,5,0), BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Controls")), BorderFactory.createEmptyBorder(2,2,2,2)))); temp.add(classifier_pane, BorderLayout.NORTH); temp.add(button_pane, BorderLayout.SOUTH); central_pane.setLayout(new BorderLayout()); central_pane.add(classifier_list_pane, BorderLayout.CENTER); central_pane.add(temp, BorderLayout.SOUTH); setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); setLayout(new BorderLayout()); add(header_pane, BorderLayout.NORTH); add(central_pane, BorderLayout.CENTER); } /** Method which acts like a destructor, tidying up references to persistant objects. */ public void destroy() { add = null; classifier = null; classifier_list = null; configure = null; instructions = null; remove = null; } /** This method is overridden to ensure the instructions are scrolled to top, before the super classes updateUI() is called. */ public void updateUI() { if(instructions != null) { instructions.setCaretPosition(0); } super.updateUI(); } /** Searches and adds a list of dynamically located CustomClassifiers. Note that the classes must be located under org.greenstone.gatherer.cdm.custom and have accompaning properties files which are used as dictionaries. * @param classifier_model An ArrayList which will be used as the model for the combobox listing all known Classifiers. */ private void addCustomClassifiers(ArrayList classifier_model) { //classifier_model.add("CustomAZList"); // Search for classifiers under the org.greenstone.gatherer.cdm.custom directory. File custom_directory = new File(Utility.BASE_DIR + "classes" + File.separator + "org" + File.separator + "greenstone" + File.separator + "gatherer" + File.separator + "cdm" + File.separator + "custom"); if(custom_directory.exists()) { File children[] = custom_directory.listFiles(); for(int i = 0; i < children.length; i++) { String temp = children[i].getName().toLowerCase(); // There are a whole bunch of conditions about what files are custom classifier main classes. if(temp.endsWith(".class") && temp.indexOf("$") == -1) { // Determine the name of this custom classifier. String name = children[i].getName(); name = name.substring(0, name.indexOf(".")); classifier_model.add(name); } } } // Search for any other CustomClassifiers within the jar file (if present) File jar_file = new File(Utility.GLI_ARCHIVE); if(jar_file.exists()) { try { JarFile jar = new JarFile(jar_file); for(Enumeration entries = jar.entries(); entries.hasMoreElements(); ) { String name = entries.nextElement().toString(); if(name.startsWith("org/greenstone/gatherer/cdm/custom/") && name.endsWith(".class") && name.indexOf("$") == -1) { name = name.substring(35, name.length() - 6); if(!classifier_model.contains(name)) { classifier_model.add(name); } } name = null; } jar = null; } catch (Exception error) { error.printStackTrace(); } } jar_file = null; } /** This class listens for actions upon the add button in the controls, and if detected calls the assignClassifier() method. */ private class AddListener implements ActionListener { /** Any implementation of ActionListener must include this method so that we can be informed when an action has occured on one of our target controls, so that we can add the selected Classifier. * @param event An ActionEvent containing information garnered from the control action. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.cdm.ArgumentConfiguration * @see org.greenstone.gatherer.cdm.Classifier * @see org.greenstone.gatherer.cdm.CustomClassifier */ public void actionPerformed(ActionEvent event) { String name = (String)classifier.getSelectedItem(); Classifier target = getClassifier(name); Classifier classifier = null; CustomClassifier custom_classifier = null; if(target != null) { classifier = target.copy(); } else { // Try to retrieve custom classifier for name. try { Class custom_class = Class.forName("org.greenstone.gatherer.cdm.custom." + name); custom_classifier = (CustomClassifier)custom_class.newInstance(); custom_classifier.setGatherer(gatherer); } catch (Exception error) { Gatherer.println("Error in ClassifierManager.AddListener.actionPerformed(): " + error); Gatherer.printStackTrace(error); } // And if all else fails create a new classifier. if(classifier == null && custom_classifier == null) { classifier = new Classifier(name, "", null); } } if(classifier != null) { // Automatically chain to configuration. This ensures required arguments are filled out. ArgumentConfiguration ac = new ArgumentConfiguration(gatherer, manager, classifier); if(ac.display()) { assignClassifier(classifier); } ac.destroy(); ac = null; } // Custom classifier else { // Spawn a new thread to handle this, as custom_classifiers can be processor heavy. CustomClassifierTask task = new CustomClassifierTask(custom_classifier); task.start(); } } } /** Listens for double clicks apon the list and react as if the configure button was pushed. */ private class ClickListener extends MouseAdapter { /** Called whenever the mouse is clicked over a registered component, we use this to chain through to the configure prompt. * @param event A MouseEvent containing information about the mouse click. */ public void mouseClicked(MouseEvent event) { if(event.getClickCount() == 2 ) { if(!classifier_list.isSelectionEmpty()) { Object object = classifier_list.getSelectedValue(); if(object instanceof Classifier) { ArgumentConfiguration ac = new ArgumentConfiguration(gatherer, manager, (Classifier)object); if(ac.display()) { assigned.refresh(); } ac.destroy(); ac = null; } else if(object instanceof CustomClassifier) { CustomClassifier cc = (CustomClassifier)object; if(cc.display(true)) { assigned.refresh(); } cc.hide(); // Remove gui prompt or else. cc = null; } } } } } /** This class listens for actions upon the configure button in the controls, and if detected creates a new ArgumentConfiguration dialog box to allow for configuration. */ private class ConfigureListener implements ActionListener { /** Any implementation of ActionListener must include this method so that we can be informed when an action has occured on one of our target controls. * @param event An ActionEvent containing information garnered from the control action. * @see org.greenstone.gatherer.cdm.ArgumentConfiguration * @see org.greenstone.gatherer.cdm.Classifier * @see org.greenstone.gatherer.cdm.CustomClassifier */ public void actionPerformed(ActionEvent event) { if(!classifier_list.isSelectionEmpty()) { Object object = classifier_list.getSelectedValue(); if(object instanceof Classifier) { ArgumentConfiguration ac = new ArgumentConfiguration(gatherer, manager, (Classifier)object); if(ac.display()) { assigned.refresh(); } ac.destroy(); ac = null; } else if(object instanceof CustomClassifier) { CustomClassifier cc = (CustomClassifier)object; if(cc.display(true)) { assigned.refresh(); } cc.hide(); // Remove gui prompt or else. cc = null; } } } } private class CustomClassifierTask extends Thread { private CustomClassifier custom_classifier; CustomClassifierTask(CustomClassifier custom_classifier) { this.custom_classifier = custom_classifier; } public void run() { if(custom_classifier.display(true)) { assignClassifier(custom_classifier); } custom_classifier.hide(); // Remove gui prompt or else. custom_classifier = null; } } /** This class listens for actions upon the remove button in the controls, and if detected calls the removeClassifier() method. */ private class RemoveListener implements ActionListener { /** Any implementation of ActionListener must include this method so that we can be informed when an action has occured on one of our target controls, so we can remove the selected Classifier. * @param event An ActionEvent containing information garnered from the control action. * @see org.greenstone.gatherer.cdm.Classifier * @see org.greenstone.gatherer.cdm.CustomClassifier */ public void actionPerformed(ActionEvent event) { if(!classifier_list.isSelectionEmpty()) { Object object = classifier_list.getSelectedValue(); if(object instanceof Classifier || object instanceof CustomClassifier) { removeClassifier(object); } } } } } }