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

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

2030124: Argument parsing now doesn't begin until the '<?xml' is detected, so as to avoid poorly formed XML errors because of PERL messages. Classifier argument harvesting could fail silently or give less than useful error messages. Now it displays a handy message dialog. How handy.

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