source: trunk/gli/src/org/greenstone/gatherer/cdm/ClassifierManager.java@ 4838

Last change on this file since 4838 was 4838, checked in by jmt12, 21 years ago

2030159: Had confused element name and identifier. Also added ability for ElementWrappers to be stored as Argument values, and thus have identifier appear within GLI while the name is written to the collect.cfg

  • Property svn:keywords set to Author Date Id Revision
File size: 40.1 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <BR><BR>
9 *
10 * Author: John Thompson, Greenstone Digital Library, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 1999 New Zealand Digital Library Project
15 *
16 * <BR><BR>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * <BR><BR>
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * <BR><BR>
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *########################################################################
36 */
37package org.greenstone.gatherer.cdm;
38
39import java.awt.BorderLayout;
40import java.awt.Color;
41import java.awt.Component;
42import java.awt.Dimension;
43import java.awt.GridLayout;
44import java.awt.event.ActionEvent;
45import java.awt.event.ActionListener;
46import java.awt.event.MouseAdapter;
47import java.awt.event.MouseEvent;
48import java.io.BufferedReader;
49import java.io.File;
50import java.io.FileInputStream;
51import java.io.FileOutputStream;
52import java.io.InputStream;
53import java.io.InputStreamReader;
54import java.io.ObjectInputStream;
55import java.io.ObjectOutputStream;
56import java.io.StringReader;
57import java.lang.Exception;
58import java.util.ArrayList;
59import java.util.Collections;
60import java.util.Enumeration;
61import java.util.jar.JarFile;
62import javax.swing.BorderFactory;
63import javax.swing.DefaultListCellRenderer;
64import javax.swing.JButton;
65import javax.swing.JComboBox;
66import javax.swing.JLabel;
67import javax.swing.JList;
68import javax.swing.JOptionPane;
69import javax.swing.JPanel;
70import javax.swing.JScrollPane;
71import javax.swing.JTextArea;
72import javax.swing.ListSelectionModel;
73import javax.swing.event.ListSelectionEvent;
74import javax.swing.event.ListSelectionListener;
75import org.apache.xerces.parsers.DOMParser;
76import org.greenstone.gatherer.Gatherer;
77import org.greenstone.gatherer.cdm.Argument;
78import org.greenstone.gatherer.cdm.CollectionDesignManager;
79import org.greenstone.gatherer.cdm.CommandTokenizer;
80import org.greenstone.gatherer.cdm.Classifier;
81import org.greenstone.gatherer.cdm.CustomClassifier;
82import org.greenstone.gatherer.cdm.DynamicListModel;
83import org.greenstone.gatherer.file.FileNode;
84import org.greenstone.gatherer.msm.ElementWrapper;
85import org.greenstone.gatherer.msm.MSMEvent;
86import org.greenstone.gatherer.msm.MSMListener;
87import org.greenstone.gatherer.msm.MSMUtils;
88import org.greenstone.gatherer.util.Utility;
89import org.w3c.dom.Document;
90import org.w3c.dom.Node;
91import org.xml.sax.InputSource;
92/** This class is responsible for keeping track of all the classifiers assigned to this collection, and providing methods for adding and removing them.
93 * @author John Thompson, Greenstone Digital Library, University of Waikato
94 * @version 2.3
95 */
96// ####################################################################################
97// Optimization Saving
98// ####################################################################################
99// Vector -> ArrayList + Memory, + Processor, (pos. - Processor)
100// Unnecessary global references + Memory (5Kb+)
101// ####################################################################################
102public class ClassifierManager
103 implements MSMListener {
104 /** An interface to the Gatherer, the creator of this cdm module, for access to the Greenstone installation directory. */
105 private Gatherer gatherer = null;
106 /** A reference to the CollectionDesignManager for access to other configuration managers. */
107 private CollectionDesignManager manager = null;
108 /** The controls for editing the contents of this manager. */
109 private Control controls = null;
110 /** A list of assigned classifiers. */
111 private DynamicListModel assigned = null;
112 /** A list of known, but currently unassigned, classifiers. */
113 private DynamicListModel reserve = null;
114 /** 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. */
115 private ArrayList unresolved_commands = null;
116 /** Constructor.
117 * @param gatherer A reference to the <strong>Gatherer</strong> for access to the Dictionary.
118 * @param manager A reference to the <strong>CollectionDesignManager</strong> itself.
119 * @see org.greenstone.gatherer.Gatherer
120 * @see org.greenstone.gatherer.cdm.DynamicListModel
121 * @see org.greenstone.gatherer.collection.CollectionManager
122 * @see org.greenstone.gatherer.msm.MetadataSetManager
123 * @see org.greenstone.gatherer.msm.MSMListener
124 */
125 public ClassifierManager(Gatherer gatherer, CollectionDesignManager manager) {
126 this.assigned = new DynamicListModel();
127 this.gatherer = gatherer;
128 this.manager = manager;
129 this.unresolved_commands = new ArrayList();
130 loadClassifiers();
131 saveClassifiers();
132 // Register as a MSMListener.
133 Gatherer.c_man.getCollection().msm.addMSMListener(this);
134 }
135 /** Method to add a new classifier to reserve.
136 * @param classifier The new <strong>Classifier</strong>.
137 * @see org.greenstone.gatherer.cdm.DynamicListModel
138 */
139 public void addClassifier(Classifier classifier) {
140 if(!reserve.contains(classifier)) {
141 reserve.addElement(classifier);
142 }
143 }
144 /** Method to assign a classifier.
145 * @param classifier The reserve <strong>Classifier</strong> to assign.
146 * @see org.greenstone.gatherer.cdm.DynamicListModel
147 */
148 public void assignClassifier(Classifier classifier) {
149 if(!assigned.contains(classifier)) {
150 assigned.addElement(classifier);
151 classifier.setManager(this);
152 gatherer.c_man.configurationChanged();
153 }
154 }
155 /** Method to assign a classifier.
156 * @param classifier The <strong>CustomClassifier</strong> to assign.
157 * @see org.greenstone.gatherer.cdm.DynamicListModel
158 */
159 public void assignClassifier(CustomClassifier classifier) {
160 if(!assigned.contains(classifier)) {
161 assigned.addElement(classifier);
162 classifier.setManager(this);
163 gatherer.c_man.configurationChanged();
164 }
165 }
166 /** Destructor.
167 * @see org.greenstone.gatherer.Gatherer
168 * @see org.greenstone.gatherer.cdm.CollectionDesignManager
169 * @see org.greenstone.gatherer.cdm.DynamicListModel
170 */
171 public void destroy() {
172 // Deregister as a listener
173 if(gatherer.c_man != null && gatherer.c_man.msm != null) {
174 gatherer.c_man.msm.removeMSMListener(this);
175 }
176 // Null globals
177 assigned = null;
178 controls = null;
179 gatherer = null;
180 manager = null;
181 reserve = null;
182 unresolved_commands = null;
183 }
184 /** Method to retrieve the classifier with the given index.
185 * @param index The index of the desired classifier as an <i>int</i>.
186 * @return The requested Classifier as an <strong>Object</strong> or <i>null</i> if no such classifier exists.
187 * @see org.greenstone.gatherer.cdm.DynamicListModel
188 */
189 public Object getClassifier(int index) {
190 if(0 <= index && index < assigned.size()) {
191 return assigned.get(index);
192 }
193 return null;
194 }
195 /** Method to retrieve the named classifier.
196 * @param name The name of the desired classifier as a <strong>String</strong>.
197 * @return The requested <strong>Classifier</strong> or <i>null</i> if no such classifier exists.
198 * @see org.greenstone.gatherer.cdm.DynamicListModel
199 */
200 public Classifier getClassifier(String name) {
201 for(int i = 0; i < reserve.size(); i++) {
202 Classifier classifier = (Classifier)reserve.get(i);
203 if(classifier.getName().equals(name)) {
204 return classifier;
205 }
206 }
207 // No success.
208 return null;
209 }
210 /** Method to retrieve the control for this manager.
211 * @return A <strong>JPanel</strong> containing the controls.
212 */
213 public JPanel getControls() {
214 if(controls == null) {
215 controls = new Control();
216 }
217 return controls;
218 }
219 /** Called whenever a metadata element changes significantly.
220 * @param event A <strong>MSMEvent</strong> choc' full of event informationy goodness.
221 */
222 public void elementChanged(MSMEvent event) {
223 // Don't really care, as the elements dealt with here are all live references so changes like identifier change will propagate immediately.
224 }
225 /** Method to find the index of the given classifier within the assigned classifiers.
226 * @param classifier The <strong>Classifier</strong> whose index you wish to find.
227 * @return The index of the classifier as an <i>int</i>, which has a value of -1 if the classifier was not found.
228 * @see org.greenstone.gatherer.cdm.DynamicListModel
229 */
230 public int indexOf(Classifier classifier) {
231 for(int i = 0; i < assigned.size(); i++) {
232 Object elem = assigned.get(i);
233 if (elem instanceof Classifier) {
234 Classifier sibling = (Classifier)elem;
235 if(sibling.equals(classifier)) {
236 return i;
237 }
238 }
239 }
240 return -1;
241 }
242 // these two methods assume that a custome classifier can never be the same as a classifier
243 public int indexOf(CustomClassifier classifier) {
244 for(int i = 0; i < assigned.size(); i++) {
245 Object elem = assigned.get(i);
246 if (elem instanceof CustomClassifier) {
247 CustomClassifier sibling = (CustomClassifier) assigned.get(i);
248 if(sibling.equals(classifier)) {
249 return i;
250 }
251 }
252 }
253 return -1;
254 }
255 /** Method to invalidate controls after a significant change in the system state.
256 */
257 public void invalidateControls() {
258 if(controls != null) {
259 controls.destroy();
260 }
261 controls = null;
262 }
263 /** Method to load the details of a single plug-in.
264 * @param classifier The classifier <strong>File</strong> you wish to load.
265 */
266 public void loadClassifier(File classifier) {
267 ///ystem.err.println("Attempting to parse " + classifier.toString());
268 Document document = null;
269 long start;
270 long end;
271 // Run classinfo on this classifier, and then send the results for parsing.
272 try {
273 String args[] = null;
274 if(Utility.isWindows()) {
275 args = new String[4];
276 if(gatherer.config.perl_path != null) {
277 args[0] = gatherer.config.perl_path;
278 }
279 else {
280 args[0] = "Perl.exe";
281 }
282 args[1] = gatherer.config.gsdl_path + "bin" + File.separator + "script" + File.separator + "classinfo.pl";
283 args[2] = "-xml";
284 args[3] = getClassifierName(classifier);
285 }
286 else {
287 args = new String[3];
288 args[0] = "classinfo.pl";
289 args[1] = "-xml";
290 args[2] = getClassifierName(classifier);
291 }
292
293 // Create the process.
294 Runtime runtime = Runtime.getRuntime();
295 Process process = runtime.exec(args);
296 BufferedReader error_in = new BufferedReader(new InputStreamReader(process.getErrorStream()));
297 String line = "";
298 StringBuffer xml = new StringBuffer("");
299 boolean xml_content = false;
300 while((line = error_in.readLine()) != null) {
301 if(xml_content) {
302 xml.append(line);
303 xml.append("\n");
304 }
305 else if(line.trim().startsWith("<?xml")) {
306 xml_content = true;
307 xml.append(line);
308 xml.append("\n");
309 }
310 }
311 error_in = null;
312 process = null;
313 runtime = null;
314 // If something has gone horribly wrong then xml will be empty.
315 if(xml.length() != 0) {
316 // Then read the xml from the piped input stream.
317 InputSource source = new InputSource(new StringReader(xml.toString()));
318 DOMParser parser = new DOMParser();
319 parser.parse(source);
320 document = parser.getDocument();
321 parser = null;
322 source = null;
323 }
324 else {
325 String classifier_name = getClassifierName(classifier);
326 Gatherer.println("Zero length argument xml detected for: " + classifier_name);
327 JOptionPane.showMessageDialog(Gatherer.g_man, get("Classifier_XML_Parse_Failed", classifier_name), get("General.Error"), JOptionPane.ERROR_MESSAGE);
328 classifier_name = null;
329 }
330 }
331 catch (Exception error) {
332 error.printStackTrace();
333 ///ystem.err.println("Error: Cannot parse " + getClassifierName(classifier));
334 }
335 if(document != null) {
336 parse(document.getDocumentElement());
337 }
338 }
339 /** Called whenever the metadata value associated to a certain record changes. */
340 public void metadataChanged(MSMEvent event) {
341 FileNode record = event.getRecord();
342 if(record != null) {
343 for(int i = 0; i < assigned.size(); i++) {
344 Object object = assigned.get(i);
345 if(object instanceof CustomClassifier) {
346 CustomClassifier classifier = (CustomClassifier) object;
347 if(!classifier.isProcessing()) { // are we already in the middle of processing??
348 classifier.process(record);
349 }
350 }
351 }
352 }
353 }
354 /** This method attempts to parse a classifier command from a command string taken from the collection configuration file. This process is quite complex as not only must the correct classifier be matched but also all of the parameters given must be legal. If such a command is found, the classifier is immediately assigned.
355 * @param command The command <strong>String</strong> that may include classifier information.
356 * @return <i>true</i> if a classifier command was parsed, <i>false</i> otherwise.
357 * @see org.greenstone.gatherer.cdm.Argument
358 * @see org.greenstone.gatherer.cdm.Classifier
359 * @see org.greenstone.gatherer.cdm.CommandTokenizer
360 */
361 public boolean parse(String command) {
362 String command_lc = command.toLowerCase();
363 if(command_lc.startsWith("classify")) {
364 CommandTokenizer tokenizer = new CommandTokenizer(command);
365 if(tokenizer.countTokens() >= 2) {
366 tokenizer.nextToken(); // Throw away 'classifier'
367 String name = tokenizer.nextToken();
368 // Try to locate the classifier with this name.
369 Classifier classifier = getClassifier(name);
370 // And if successful start to parse the arguments.
371 if(classifier != null) {
372 // Take a copy.
373 classifier = classifier.copy();
374 String key = null;
375 while((key = tokenizer.nextToken()) != null) {
376 // Try to retrieve a matching argument.
377 Argument argument = classifier.getArgument(key);
378 if(argument != null) {
379 // Set as assigned.
380 argument.setAssigned(true);
381 // And if the argument is of a parameter type, parse a parameter.
382 if(argument.getType() != Argument.FLAG && tokenizer.hasMoreTokens()) {
383 String value = tokenizer.nextToken();
384 ElementWrapper element = null;
385 // special check for metadata
386 if (argument.getType() == Argument.METADATA) {
387 value = value.replace(':', MSMUtils.NS_SEP);
388 if (value.indexOf(MSMUtils.NS_SEP)==-1){
389 value = Utility.EXTRACTED_METADATA_NAMESPACE + MSMUtils.NS_SEP + value;
390 }
391 // Now retrieve the element this refers to, if available.
392 element = Gatherer.c_man.getCollection().msm.getElement(value);
393 }
394 if(element != null) {
395 argument.setElementValue(element);
396 element = null;
397 }
398 else {
399 argument.setValue(value);
400 }
401 }
402 }
403 // Argument cannot be matched.
404 else {
405 String cur_key = key;
406 String value = tokenizer.nextToken();
407 if(value.startsWith("-")) {
408 key = value;
409 value = null;
410 }
411 else {
412 key = null;
413 }
414 String custom = classifier.getCustom();
415 if(custom == null) {
416 if(value == null) {
417 classifier.setCustom(cur_key);
418 }
419 else {
420 classifier.setCustom(cur_key + " " + value);
421 }
422 }
423 else {
424 if(value == null) {
425 classifier.setCustom(custom + " " + cur_key);
426 }
427 else {
428 classifier.setCustom(custom + " " + cur_key + " " + value);
429 }
430 }
431 }
432 }
433 assignClassifier(classifier);
434 return true;
435 }
436 else {
437 ///ystem.err.println("Unknown classifier");
438 }
439 }
440 }
441 else if(command_lc.startsWith("customclassifier")) {
442 unresolved_commands.add(command);
443 return true;
444 }
445 return false;
446 }
447 /** 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.
448 * @param classifier The Classifier or CustomClassifier, as an <strong>Object</strong>, to remove.
449 * @see org.greenstone.gatherer.cdm.DynamicListModel
450 */
451 public void removeClassifier(Object classifier) {
452 assigned.removeElement(classifier);
453 gatherer.c_man.configurationChanged();
454 }
455 /** Method which attempts to reparse obvious classifier commands which previously referenced unresovable Classifiers.
456 * @see org.greenstone.gatherer.cdm.Classifier
457 * @see org.greenstone.gatherer.cdm.CommandTokenizer
458 * @see org.greenstone.gatherer.cdm.CustomClassifier
459 */
460 public void reparseUnresolved() {
461 for(int i = 0; i < unresolved_commands.size(); i++) {
462 String command = (String) unresolved_commands.get(i);
463 CommandTokenizer tokenizer = new CommandTokenizer(command);
464 if(tokenizer.countTokens() >= 6) {
465 tokenizer.nextToken();// Lose customclassifier
466 // Get class name.
467 String class_name = tokenizer.nextToken();
468 // Parse arguments.
469 String replaces = null;
470 String separations = null;
471 while(tokenizer.hasMoreTokens()) {
472 String arg_name = tokenizer.nextToken();
473 if(arg_name.equalsIgnoreCase("-replaces")) {
474 replaces = tokenizer.nextToken();
475 }
476 else if (arg_name.equalsIgnoreCase("-separations")){
477 separations = tokenizer.nextToken();
478 }
479 }
480 try {
481 replaces = replaces.substring(2);
482 int index = Integer.parseInt(replaces);
483 Classifier original = (Classifier)getClassifier(index);
484 if(original != null) {
485 Class custom_classifier_class = Class.forName("org.greenstone.gatherer.cdm.custom." + class_name);
486 CustomClassifier custom_classifier = (CustomClassifier) custom_classifier_class.newInstance();
487 custom_classifier.setGatherer(gatherer);
488 custom_classifier.recreate(original, separations);
489 assigned.add(index, custom_classifier);
490 assigned.removeElement(original);
491 }
492 else {
493 ///ystem.err.println("Missing original.");
494 }
495 }
496 catch (Exception error) {
497 error.printStackTrace();
498 }
499 }
500 }
501 // Regardless of if they work, clear the commands.
502 unresolved_commands.clear();
503 }
504 /** Method to cache the current contents of reserve (known classifiers) to file.
505 * @see org.greenstone.gatherer.util.Utility
506 */
507 public void saveClassifiers() {
508 try {
509 FileOutputStream file = new FileOutputStream(Utility.BASE_DIR + "classifiers.dat");
510 ObjectOutputStream out = new ObjectOutputStream(file);
511 out.writeObject(reserve);
512 out.close();
513 }
514 catch (Exception error) {
515 }
516 }
517 /** Called when a metadata set changed significantly.
518 * @param event A <strong>MSMEvent</strong> containing information about the set change.
519 */
520 public void setChanged(MSMEvent event) {
521 // 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.
522 }
523 /** Method used to determine the number of classifiers that have been assigned.
524 * @return An <i>int</i> which is the number of classifiers.
525 */
526 public int size() {
527 return assigned.size();
528 }
529 /** Method to print out a block of classifier commands, much like you'd find in a collection configuration file.
530 * @return A <strong>String</strong> containing a series of classifier commands separated by new lines.
531 * @see org.greenstone.gatherer.cdm.Classifier
532 * @see org.greenstone.gatherer.cdm.CustomClassifier
533 */
534 public String toString() {
535 StringBuffer text = new StringBuffer();
536 for(int i = 0; i < assigned.size(); i++) {
537 Object object = assigned.get(i);
538 if(object instanceof Classifier) {
539 Classifier classifier = (Classifier) object;
540 text.append(classifier.toStringConfig());
541 }
542 else if(object instanceof CustomClassifier) {
543 CustomClassifier classifier = (CustomClassifier) object;
544 text.append(classifier.getCommand());
545 text.append("\n");
546 text.append(classifier.getCustomCommand(i));
547 }
548 text.append("\n");
549 }
550 text.append("\n");
551 return text.toString();
552 }
553 /** Called when a significant change has occured to a value tree for a certain element, however we take no further action.
554 * @param event A <strong>MSMEvent</strong> containing information relevant to the event.
555 */
556 public void valueChanged(MSMEvent event) {
557 }
558 /** Retrieve a phrase from the dictionary based on a certain key.
559 * @param key The search <strong>String</strong>.
560 * @return The matching phrase from the Dictionary.
561 */
562 private String get(String key) {
563 return get(key, null);
564 }
565 /** Retrieve a phrase from the dictionary based on a certain key and certain arguments.
566 * @param key The search <strong>String</strong>.
567 * @param args A <strong>String[]</strong> used to complete and format the returned phrase.
568 * @return The matching phrase from the Dictionary.
569 * @see org.greenstone.gatherer.Dictionary
570 * @see org.greenstone.gatherer.Gatherer
571 */
572 private String get(String key, String arg) {
573 String[] args = null;
574 if(arg != null) {
575 args = new String[1];
576 args[0] = arg;
577 }
578 if(key.indexOf(".") == -1) {
579 key = "CDM.ClassifierManager." + key;
580 }
581 return gatherer.dictionary.get(key, args);
582 }
583 /** Method to extract just the classifiers name from a file object.
584 * @param classifier The <strong>File</strong> which references a certain classifier.
585 * @return A <strong>String</strong> containing just the classifiers name, without extension.
586 */
587 private String getClassifierName(File classifier) {
588 String name = classifier.getName();
589 if(name.indexOf(".") != -1) {
590 name = name.substring(0, name.indexOf("."));
591 }
592 return name;
593 }
594 /** Method to initially load information from the standard plug-ins within the gsdl Perl library.
595 * @see org.greenstone.gatherer.cdm.DynamicListModel
596 * @see org.greenstone.gatherer.util.Utility
597 */
598 private void loadClassifiers() {
599 // Attempt to restore the cached file.
600 try {
601 FileInputStream file = new FileInputStream(Utility.BASE_DIR + "classifiers.dat");
602 ObjectInputStream input = new ObjectInputStream(file);
603 reserve = (DynamicListModel) input.readObject();
604 }
605 catch (Exception error) {
606 }
607 if(reserve == null) {
608 reserve = new DynamicListModel();
609 // Retrieve the gsdl home directory...
610 String directory = gatherer.config.gsdl_path;
611 directory = directory + "perllib" + File.separator + "classify" + File.separator;
612 loadClassifiers(new File(directory));
613 }
614 }
615 /** Method to load plug-in information from a specified directory. Of course no plug-ins may be found at this location.
616 * @param directory A <strong>File</strong> indicating the directory to be scanned for plug-ins.
617 * @see org.greenstone.gatherer.cdm.ParsingProgress
618 */
619 private void loadClassifiers(File directory) {
620 File files[] = directory.listFiles();
621 if(files != null) {
622 // Create a progress indicator.
623 ParsingProgress progress = new ParsingProgress(get("CDM.ClassifierManager.Parsing.Title"), get("CDM.ClassifierManager.Parsing.Message"), files.length);
624 for(int i = 0; i < files.length; i++) {
625 // We only want to check Perl Modules.
626 if(files[i].getName().endsWith(".pm")) {
627 loadClassifier(files[i]);
628 }
629 progress.inc();
630 }
631 progress.dispose();
632 progress.destroy();
633 progress = null;
634 }
635 }
636 /** Parses a DOM tree model turning it into a Classifier and its associated arguments.
637 * @param root The <strong>Node</strong> at the root of the DOM model.
638 * @return A newly created <strong>Classifier</strong> based on the information parsed from the DOM model.
639 * @see org.greenstone.gatherer.cdm.Argument
640 */
641 private Classifier parse(Node root) {
642 Classifier classifier = new Classifier();
643 String node_name = null;
644 for(Node node = root.getFirstChild(); node != null;
645 node = node.getNextSibling()) {
646 node_name = node.getNodeName();
647 if(node_name.equals("Name")) {
648 String name = MSMUtils.getValue(node);
649 // 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.
650 Classifier existing = getClassifier(name);
651 if(existing != null) {
652 return existing;
653 }
654 classifier.setName(name);
655 }
656 else if(node_name.equals("Desc")) {
657 classifier.setDesc(MSMUtils.getValue(node));
658 }
659 // Parse the multitude of arguments.
660 else if(node_name.equals("Arguments")) {
661 for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) {
662 node_name = arg.getNodeName();
663 // An option.
664 if(node_name.equals("Option")) {
665 Argument argument = new Argument();
666 // If its an option we parse the multitude of details an options might have.
667 for(Node det = arg.getFirstChild(); det != null; det = det.getNextSibling()) {
668 node_name = det.getNodeName();
669 if(node_name.equals("Name")) {
670 argument.setName(MSMUtils.getValue(det));
671 }
672 else if(node_name.equals("Desc")) {
673 argument.setDesc(MSMUtils.getValue(det));
674 }
675 else if(node_name.equals("Type")) {
676 argument.setType(MSMUtils.getValue(det));
677 }
678 else if(node_name.equals("Default")) {
679 argument.setDefault(MSMUtils.getValue(det));
680 }
681 else if(node_name.equals("List")) {
682 // Two final loops are required to parse lists.
683 for(Node value = det.getFirstChild(); value != null; value = value.getNextSibling()) {
684 if(value.getNodeName().equals("Value")) {
685 String key = null;
686 String desc = "";
687 for(Node subvalue = value.getFirstChild(); subvalue != null; subvalue = subvalue.getNextSibling()) {
688 node_name = subvalue.getNodeName();
689 if(node_name.equals("Name")) {
690 key = MSMUtils.getValue(subvalue);
691 }
692 else if(node_name.equals("Desc")) {
693 desc = MSMUtils.getValue(subvalue);
694 }
695 }
696 if(key != null) {
697 argument.addOption(key, desc);
698 }
699 }
700 }
701 }
702 else if(node_name.equals("Required")) {
703 String v = MSMUtils.getValue(det);
704 ///ystem.err.println("Required = " + v);
705 if(v.equalsIgnoreCase("yes")) {
706 ///ystem.err.println("Setting required to true.");
707 argument.setRequired(true);
708 }
709 }
710 }
711 classifier.addArgument(argument);
712 }
713 // A super classifier class.
714 else if(node_name.equals("ClasInfo")) {
715 Classifier super_classifier = parse(arg);
716 classifier.setSuper(super_classifier);
717 }
718 }
719 }
720 }
721 if(classifier.getName() != null) {
722 addClassifier(classifier);
723 return classifier;
724 }
725 return null;
726 }
727 /** A class which provides controls for assigned and editing classifiers. */
728 private class Control
729 extends JPanel {
730 /** Button for adding classifiers. */
731 private JButton add = null;
732 /** Button for configuring the selected classifier. */
733 private JButton configure = null;
734 /** Button to remove the selected classifier. */
735 private JButton remove = null;
736 /** A combobox containing all of the known classifiers, including those that may have already been assigned. */
737 private JComboBox classifier = null;
738 /** A list of assigned classifiers. */
739 private JList classifier_list = null;
740 /** The text area containing instructions on the use of this control. */
741 private JTextArea instructions = null;
742 /** Constructor.
743 * @see org.greenstone.gatherer.cdm.ClassifierManager.Control.AddListener
744 * @see org.greenstone.gatherer.cdm.ClassifierManager.Control.ConfigureListener
745 * @see org.greenstone.gatherer.cdm.ClassifierManager.Control.RemoveListener
746 */
747 public Control() {
748 Object classifiers[] = reserve.toArray();
749 ArrayList classifier_model = new ArrayList();
750 for(int i = 0; i < classifiers.length; i++) {
751 classifier_model.add(((Classifier)classifiers[i]).getName());
752 }
753 // Now we add custom classifiers.
754 addCustomClassifiers(classifier_model);
755 Collections.sort(classifier_model);
756 // Create
757 add = new JButton(get("Add"));
758 JPanel button_pane = new JPanel();
759 JPanel central_pane = new JPanel();
760 configure = new JButton(get("Configure"));
761 configure.setEnabled(false);
762 JPanel header_pane = new JPanel();
763 instructions = new JTextArea(get("Instructions"));
764 instructions.setEditable(false);
765 instructions.setLineWrap(true);
766 instructions.setRows(5);
767 instructions.setWrapStyleWord(true);
768 classifier = new JComboBox(classifier_model.toArray());
769 classifier.setEditable(false);
770 JLabel classifier_label = new JLabel(get("Classifier"));
771 classifier_list = new JList(assigned);
772 classifier_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
773 JLabel classifier_list_label = new JLabel(get("Assigned"));
774 classifier_list_label.setHorizontalAlignment(JLabel.CENTER);
775 classifier_list_label.setOpaque(true);
776 JPanel classifier_list_pane = new JPanel();
777 JPanel classifier_pane = new JPanel();
778 remove = new JButton(get("Remove"));
779 remove.setEnabled(false);
780 JLabel title = new JLabel(get("Title"));
781 title.setHorizontalAlignment(JLabel.CENTER);
782 title.setOpaque(true);
783 JPanel temp = new JPanel(new BorderLayout());
784 // Listeners
785 add.addActionListener(new AddListener());
786 configure.addActionListener(new ConfigureListener());
787 remove.addActionListener(new RemoveListener());
788 classifier_list.addMouseListener(new ClickListener());
789 classifier_list.addListSelectionListener(new ListListener());
790 // Layout
791 title.setBorder(BorderFactory.createEmptyBorder(0,0,2,0));
792 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5));
793 header_pane.setLayout(new BorderLayout());
794 header_pane.add(title, BorderLayout.NORTH);
795 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER);
796 classifier_list_label.setBorder(BorderFactory.createEmptyBorder(0,2,0,2));
797 classifier_list_pane.setLayout(new BorderLayout());
798 classifier_list_pane.add(classifier_list_label, BorderLayout.NORTH);
799 classifier_list_pane.add(new JScrollPane(classifier_list), BorderLayout.CENTER);
800 classifier_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
801 classifier_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
802 classifier_pane.setLayout(new GridLayout(1,2));
803 classifier_pane.add(classifier_label);
804 classifier_pane.add(classifier);
805 button_pane.setLayout(new GridLayout(3,1));
806 button_pane.add(add);
807 button_pane.add(configure);
808 button_pane.add(remove);
809 // Scope these mad bordering skillz.
810 temp.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,0,5,0), BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Controls")), BorderFactory.createEmptyBorder(2,2,2,2))));
811 temp.add(classifier_pane, BorderLayout.NORTH);
812 temp.add(button_pane, BorderLayout.SOUTH);
813 central_pane.setLayout(new BorderLayout());
814 central_pane.add(classifier_list_pane, BorderLayout.CENTER);
815 central_pane.add(temp, BorderLayout.SOUTH);
816 setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
817 setLayout(new BorderLayout());
818 add(header_pane, BorderLayout.NORTH);
819 add(central_pane, BorderLayout.CENTER);
820 }
821 /** Method which acts like a destructor, tidying up references to persistant objects.
822 */
823 public void destroy() {
824 add = null;
825 classifier = null;
826 classifier_list = null;
827 configure = null;
828 instructions = null;
829 remove = null;
830 }
831 /** This method is overridden to ensure the instructions are scrolled to top, before the super classes updateUI() is called.
832 */
833 public void updateUI() {
834 if(instructions != null) {
835 instructions.setCaretPosition(0);
836 }
837 super.updateUI();
838 }
839 /** 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.
840 * @param classifier_model An <strong>ArrayList</strong> which will be used as the model for the combobox listing all known Classifiers.
841 */
842 private void addCustomClassifiers(ArrayList classifier_model) {
843 //classifier_model.add("CustomAZList");
844 // Search for classifiers under the org.greenstone.gatherer.cdm.custom directory.
845 File custom_directory = new File(Utility.BASE_DIR + "classes" + File.separator + "org" + File.separator + "greenstone" + File.separator + "gatherer" + File.separator + "cdm" + File.separator + "custom");
846 if(custom_directory.exists()) {
847 File children[] = custom_directory.listFiles();
848 for(int i = 0; i < children.length; i++) {
849 String temp = children[i].getName().toLowerCase();
850 // There are a whole bunch of conditions about what files are custom classifier main classes.
851 if(temp.endsWith(".class") && temp.indexOf("$") == -1) {
852 // Determine the name of this custom classifier.
853 String name = children[i].getName();
854 name = name.substring(0, name.indexOf("."));
855 classifier_model.add(name);
856 }
857 }
858 }
859 // Search for any other CustomClassifiers within the jar file (if present)
860 File jar_file = new File(Utility.GLI_ARCHIVE);
861 if(jar_file.exists()) {
862 try {
863 JarFile jar = new JarFile(jar_file);
864 for(Enumeration entries = jar.entries(); entries.hasMoreElements(); ) {
865 String name = entries.nextElement().toString();
866 if(name.startsWith("org/greenstone/gatherer/cdm/custom/") && name.endsWith(".class") && name.indexOf("$") == -1) {
867 name = name.substring(35, name.length() - 6);
868 if(!classifier_model.contains(name)) {
869 classifier_model.add(name);
870 }
871 }
872 name = null;
873 }
874 jar = null;
875 }
876 catch (Exception error) {
877 error.printStackTrace();
878 }
879 }
880 jar_file = null;
881 }
882 /** This class listens for actions upon the add button in the controls, and if detected calls the assignClassifier() method.
883 */
884 private class AddListener
885 implements ActionListener {
886 /** 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.
887 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
888 * @see org.greenstone.gatherer.Gatherer
889 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration
890 * @see org.greenstone.gatherer.cdm.Classifier
891 * @see org.greenstone.gatherer.cdm.CustomClassifier
892 */
893 public void actionPerformed(ActionEvent event) {
894 String name = (String)classifier.getSelectedItem();
895 Classifier target = getClassifier(name);
896 Classifier classifier = null;
897 CustomClassifier custom_classifier = null;
898 if(target != null) {
899 classifier = target.copy();
900 }
901 else {
902 // Try to retrieve custom classifier for name.
903 try {
904 Class custom_class = Class.forName("org.greenstone.gatherer.cdm.custom." + name);
905 custom_classifier = (CustomClassifier)custom_class.newInstance();
906 custom_classifier.setGatherer(gatherer);
907 }
908 catch (Exception error) {
909 Gatherer.println("Error in ClassifierManager.AddListener.actionPerformed(): " + error);
910 Gatherer.printStackTrace(error);
911
912 }
913 // And if all else fails create a new classifier.
914 if(classifier == null && custom_classifier == null) {
915 classifier = new Classifier(name, "", null);
916 }
917 }
918 if(classifier != null) {
919 // Automatically chain to configuration. This ensures required arguments are filled out.
920 ArgumentConfiguration ac = new ArgumentConfiguration(gatherer, manager, classifier);
921 if(ac.display()) {
922 assignClassifier(classifier);
923 }
924 ac.destroy();
925 ac = null;
926 }
927 // Custom classifier
928 else {
929 // Spawn a new thread to handle this, as custom_classifiers can be processor heavy.
930 CustomClassifierTask task = new CustomClassifierTask(custom_classifier);
931 task.start();
932 }
933 }
934 }
935 /** Listens for double clicks apon the list and react as if the configure button was pushed. */
936 private class ClickListener
937 extends MouseAdapter {
938 /** Called whenever the mouse is clicked over a registered component, we use this to chain through to the configure prompt.
939 * @param event A <strong>MouseEvent</strong> containing information about the mouse click.
940 */
941 public void mouseClicked(MouseEvent event) {
942 if(event.getClickCount() == 2 ) {
943 if(!classifier_list.isSelectionEmpty()) {
944 Object object = classifier_list.getSelectedValue();
945 if(object instanceof Classifier) {
946 ArgumentConfiguration ac = new ArgumentConfiguration(gatherer, manager, (Classifier)object);
947 if(ac.display()) {
948 assigned.refresh();
949 }
950 ac.destroy();
951 ac = null;
952 }
953 else if(object instanceof CustomClassifier) {
954 CustomClassifier cc = (CustomClassifier)object;
955 if(cc.display(true)) {
956 assigned.refresh();
957 }
958 cc.hide(); // Remove gui prompt or else.
959 cc = null;
960 }
961 }
962 }
963 }
964 }
965 /** 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.
966 */
967 private class ConfigureListener
968 implements ActionListener {
969 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured on one of our target controls.
970 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
971 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration
972 * @see org.greenstone.gatherer.cdm.Classifier
973 * @see org.greenstone.gatherer.cdm.CustomClassifier
974 */
975 public void actionPerformed(ActionEvent event) {
976 if(!classifier_list.isSelectionEmpty()) {
977 Object object = classifier_list.getSelectedValue();
978 if(object instanceof Classifier) {
979 ArgumentConfiguration ac = new ArgumentConfiguration(gatherer, manager, (Classifier)object);
980 if(ac.display()) {
981 assigned.refresh();
982 }
983 ac.destroy();
984 ac = null;
985 }
986 else if(object instanceof CustomClassifier) {
987 CustomClassifier cc = (CustomClassifier)object;
988 if(cc.display(true)) {
989 assigned.refresh();
990 }
991 cc.hide(); // Remove gui prompt or else.
992 cc = null;
993 }
994 }
995 }
996 }
997
998 private class CustomClassifierTask
999 extends Thread {
1000
1001 private CustomClassifier custom_classifier;
1002
1003 CustomClassifierTask(CustomClassifier custom_classifier) {
1004 this.custom_classifier = custom_classifier;
1005 }
1006
1007 public void run() {
1008 if(custom_classifier.display(true)) {
1009 assignClassifier(custom_classifier);
1010 }
1011 custom_classifier.hide(); // Remove gui prompt or else.
1012 custom_classifier = null;
1013 }
1014 }
1015
1016 /** listens for changes in the list selection and enables the configure and remove buttons if there is a selection, disables them if there is no selection */
1017 private class ListListener
1018 implements ListSelectionListener {
1019
1020 public void valueChanged(ListSelectionEvent e) {
1021 if (!e.getValueIsAdjusting()) { // we get two events for one change in list selection - use the false one ( the second one)
1022 if (classifier_list.isSelectionEmpty()) {
1023 configure.setEnabled(false);
1024 remove.setEnabled(false);
1025 } else {
1026 configure.setEnabled(true);
1027 remove.setEnabled(true);
1028 }
1029 }
1030 }
1031 }
1032 /** This class listens for actions upon the remove button in the controls, and if detected calls the removeClassifier() method.
1033 */
1034 private class RemoveListener
1035 implements ActionListener {
1036 /** Any implementation of <i>ActionListener</i> 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.
1037 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
1038 * @see org.greenstone.gatherer.cdm.Classifier
1039 * @see org.greenstone.gatherer.cdm.CustomClassifier
1040 */
1041 public void actionPerformed(ActionEvent event) {
1042 if(!classifier_list.isSelectionEmpty()) {
1043 Object object = classifier_list.getSelectedValue();
1044 if(object instanceof Classifier || object instanceof CustomClassifier) {
1045 removeClassifier(object);
1046 }
1047 }
1048 }
1049 }
1050 }
1051}
1052
1053
1054
1055
1056
1057
Note: See TracBrowser for help on using the repository browser.