source: trunk/gli/src/org/greenstone/gatherer/cdm/custom/CustomAZList.java@ 4656

Last change on this file since 4656 was 4656, checked in by mdewsnip, 21 years ago

Changed to use the new interface language variable in Configuration instead of the (incorrect) default language used for partition indexes.

  • Property svn:keywords set to Author Date Id Revision
File size: 34.3 KB
Line 
1package org.greenstone.gatherer.cdm.custom;
2/**
3 *#########################################################################
4 *
5 * A component of the Gatherer application, part of the Greenstone digital
6 * library suite from the New Zealand Digital Library Project at the
7 * University of Waikato, New Zealand.
8 *
9 * <BR><BR>
10 *
11 * Author: John Thompson, Greenstone Digital Library, University of Waikato
12 *
13 * <BR><BR>
14 *
15 * Copyright (C) 1999 New Zealand Digital Library Project
16 *
17 * <BR><BR>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * <BR><BR>
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * <BR><BR>
32 *
33 * You should have received a copy of the GNU General Public License
34 * along with this program; if not, write to the Free Software
35 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
36 *########################################################################
37 */
38import java.awt.*;
39import java.awt.event.*;
40import java.util.*;
41import javax.swing.*;
42import javax.swing.tree.*;
43import org.greenstone.gatherer.Gatherer;
44import org.greenstone.gatherer.cdm.Classifier;
45import org.greenstone.gatherer.cdm.ClassifierManager;
46import org.greenstone.gatherer.cdm.CustomClassifier;
47import org.greenstone.gatherer.cdm.Language;
48import org.greenstone.gatherer.file.FileNode;
49import org.greenstone.gatherer.msm.ElementWrapper;
50import org.greenstone.gatherer.msm.Metadata;
51import org.greenstone.gatherer.msm.MetadataSet;
52import org.greenstone.gatherer.msm.MetadataSetManager;
53import org.greenstone.gatherer.msm.MSMUtils;
54import org.greenstone.gatherer.util.ArrayTools;
55import org.greenstone.gatherer.util.Utility;
56import org.greenstone.gatherer.valuetree.GValueModel;
57import org.greenstone.gatherer.valuetree.GValueNode;
58import org.greenstone.gatherer.gui.SimpleMenuBar;
59import org.greenstone.gatherer.gui.ModalDialog;
60import org.w3c.dom.*;
61/** Provides the functionality for a custom AZList, including a GUI for configuration of this classifier.
62 * @author John Thompson, Greenstone Digital Library, University of Waikato
63 * @version 2.3
64 */
65final public class CustomAZList
66 implements CustomClassifier {
67 /** When this the control dialog is disposed, should we go ahead a add this custom AZ list (by processing all file records and building a new hierarchy)? */
68 private boolean process = false;
69 /** are we currently processing?? */
70 private boolean is_processing = false;
71 /** A reference to the Gatherer for access to the Collection and MetadataSetManager. */
72 private Gatherer gatherer = null;
73 /** A mapping from a String pattern to a specific GValueNode within the hidden hierarchy. */
74 private Hashtable mappings = null;
75 /** The button used to cancel the dialog. */
76 private JButton cancel = null;
77 /** The button used to confirm and close the dialog. */
78 private JButton ok = null;
79 /** The dialog which contains the controls for configuring this custom classifier. */
80 private JDialog controls = null;
81 /** The checkbox used to indicate whether there is a name given for the classifier button (in the Greenstone collections menubar). */
82 private JCheckBox buttonname_label = null;
83 /** The checkbox used to indicate that the final classifier should be sorted in some way. */
84 private JCheckBox sort_label = null;
85 /** The control allowing you to choose the assigned metadata this classifier should be based on. */
86 private JComboBox metadata = null;
87 /** The metadata the final classifier should be sorted by. */
88 private JComboBox sort = null;
89 /** A field for entering the button name. */
90 private JTextField buttonname = null;
91 /** A field which provides a clear indication of the current ranges selected for your custom classifier. */
92 private JTextField separators_preview = null;
93 /** An array of togglebuttons showing what separations are available. */
94 private JToggleButton separators[] = null;
95 /** A reference to the progress dialog if it exists. */
96 private ProgressDialog pd = null;
97 /** A private dictionary for this custom classifier. */
98 private ResourceBundle dictionary = null;
99 /** The name of the hidden metadata. We at least luck out in that we don't need a live reference to the metadata, because the user can never change it (or if they do its their own stinking fault). */
100 private String hidden_mde_name = null;
101 /** The name of this pseudo-classifier. */
102 final static public String NAME = "CustomAZList";
103 /** The size of a label. */
104 final static private Dimension LABEL_SIZE = new Dimension(200,25);
105 /** The size of the controls for this pseudo-classifier. */
106 final static private Dimension SIZE = new Dimension(550,425);
107 /** An array of values for the separators. */
108 final static private String VALUES[] = {"Numbers", "A", "B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
109 /** The same array as above, except as integer values (note that "Numbers" becomes "#"). */
110 static public int INT_VALUES[] = {'#','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
111 /** A third array which is almost the same as the others (can anyone else say poor code reuse) but subsitutes "#" for "Numbers". */
112 static public String STRING_VALUES[] = {"#","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
113 /** Default Constructor. Needed to load this class dynamically. */
114 public CustomAZList() {}
115 /** Constructor.
116 * @param gatherer A reference to the <strong>Gatherer</strong>.
117 */
118 public CustomAZList(Gatherer gatherer) {
119 this.gatherer = gatherer;
120 }
121 /** Used to compare this classifier to another classifier for the purposes of ordering.
122 * @param object The other classifier as an <strong>Object</strong>.
123 * @see org.greenstone.gatherer.cdm.Classifier
124 * @see org.greenstone.gatherer.cdm.CustomClassifier
125 */
126 public int compareTo(Object object) {
127 if(object instanceof Classifier) {
128 Classifier classifier = (Classifier) object;
129 return NAME.compareTo(classifier.getName());
130 }
131 else if(object instanceof CustomClassifier) {
132 CustomClassifier classifier = (CustomClassifier) object;
133 return NAME.compareTo(classifier.getName());
134 }
135 return NAME.compareTo(object.toString());
136 }
137 /** Ensure that the dialog can be correctly garbage collected. */
138 public void destroy() {
139 controls = null;
140 }
141 public void hide() {
142 controls.setVisible(false);
143 }
144 /** Produce a new copy of this custom classifier. Remember that what the classifier manager does is create instances of all possible classifiers, then as they are assigned creates and assigns copies of the original reserve. This way we only have to parse arguments once.
145 * @return A new <strong>CustomClassifier</strong> which is a copy of this one.
146 * @see org.greenstone.gatherer.Gatherer
147 */
148 public CustomClassifier copy() {
149 return new CustomAZList(gatherer);
150 }
151 /** Show the controls for configuring this pseudo-classifier.
152 * @param show <i>true</i> to actually display the configuration dialog on screen, <i>false</i> to do everything except show the control and process the data (useful for setting the control up and/or reloading custom classifiers from collect.cfg).
153 * @see org.greenstone.gatherer.Configuration
154 * @see org.greenstone.gatherer.Gatherer
155 * @see org.greenstone.gatherer.cdm.custom.CustomAZList.ButtonNameListener
156 * @see org.greenstone.gatherer.cdm.custom.CustomAZList.CancelListener
157 * @see org.greenstone.gatherer.cdm.custom.CustomAZList.OKListener
158 * @see org.greenstone.gatherer.cdm.custom.CustomAZList.SeparatorListener
159 * @see org.greenstone.gatherer.cdm.custom.CustomAZList.SortListener
160 * @see org.greenstone.gatherer.collection.CollectionManager
161 * @see org.greenstone.gatherer.msm.ElementWrapper
162 * @see org.greenstone.gatherer.msm.MetadataSetManager
163 */
164 public boolean display(boolean show) {
165 if(controls == null) {
166
167 Vector elements = gatherer.c_man.getCollection().msm.getAssignedElements();
168 // Creation
169 controls = new ModalDialog(gatherer.g_man);
170 controls.setModal(true);
171 controls.setSize(SIZE);
172 controls.setTitle(get("Title_JDialog"));
173 controls.setJMenuBar(new SimpleMenuBar("7.7"));
174 JPanel content_pane = (JPanel) controls.getContentPane();
175 JPanel upper_pane = new JPanel();
176 JPanel metadata_pane = new JPanel();
177 JLabel metadata_label = new JLabel(get("Metadata_JLabel"));
178 metadata_label.setPreferredSize(LABEL_SIZE);
179 metadata = new JComboBox(elements);
180 JPanel buttonname_pane = new JPanel();
181 buttonname_label = new JCheckBox(get("Buttonname_JCheckBox"));
182 buttonname_label.setPreferredSize(LABEL_SIZE);
183 buttonname = new JTextField();
184 buttonname.setBackground(Color.lightGray);
185 buttonname.setEnabled(false);
186 JPanel sort_pane = new JPanel();
187 sort_label = new JCheckBox(get("Sort_JLabel"));
188 sort = new JComboBox(elements);
189 JPanel separations_pane = new JPanel();
190 JLabel separations_label = new JLabel(get("Separations_JLabel"));
191 separations_label.setPreferredSize(LABEL_SIZE);
192 separators_preview = new JTextField();
193 separators_preview.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
194 separators_preview.setEnabled(false); // View only.
195 separators_preview.setForeground(Color.black);
196 JPanel lower_pane = new JPanel();
197 JPanel button_pane = new JPanel();
198 ok = new JButton(get("OK_JButton"));
199 cancel = new JButton(get("Cancel_JButton"));
200 // Connection
201 buttonname_label.addActionListener(new ButtonNameListener());
202 cancel.addActionListener(new CancelListener());
203 ok.addActionListener(new OKListener());
204 sort_label.addActionListener(new SortListener());
205 // The Toggle Button block. Done all at once so we don't end up with three for loops.
206 JPanel center_pane = new JPanel();
207 center_pane.setLayout(new GridLayout(3, 9));
208 separators = new JToggleButton[VALUES.length];
209 SeparatorListener sl = new SeparatorListener();
210 for(int i = 0; i < VALUES.length; i++) {
211 separators[i] = new JToggleButton(get(VALUES[i]));
212 separators[i].addActionListener(sl);
213 if(i != 0) {
214 center_pane.add(separators[i]);
215 }
216 else {
217 JLabel temp = new JLabel(get(VALUES[0]));
218 temp.setHorizontalAlignment(JLabel.CENTER);
219 center_pane.add(temp);
220 }
221 }
222 separators_preview.setText(getPreview(true));
223 // Layout
224 metadata_pane.setBorder(BorderFactory.createEmptyBorder(1,0,1,0));
225 metadata_pane.setLayout(new BorderLayout());
226 metadata_pane.add(metadata_label, BorderLayout.WEST);
227 metadata_pane.add(metadata, BorderLayout.CENTER);
228
229 buttonname_pane.setBorder(BorderFactory.createEmptyBorder(1,0,1,0));
230 buttonname_pane.setLayout(new BorderLayout());
231 buttonname_pane.add(buttonname_label, BorderLayout.WEST);
232 buttonname_pane.add(buttonname, BorderLayout.CENTER);
233
234 sort_pane.setBorder(BorderFactory.createEmptyBorder(1,0,1,0));
235 sort_pane.setLayout(new BorderLayout());
236 sort_pane.add(sort_label, BorderLayout.WEST);
237 sort_pane.add(sort, BorderLayout.CENTER);
238
239 upper_pane.setBorder(BorderFactory.createEmptyBorder(0,0,4,0));
240 upper_pane.setLayout(new GridLayout(3, 1));
241 upper_pane.add(metadata_pane);
242 upper_pane.add(buttonname_pane);
243 upper_pane.add(separations_label);
244
245 separations_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,0,5,0), BorderFactory.createRaisedBevelBorder()));
246 separations_pane.setLayout(new BorderLayout());
247 separations_pane.add(separators_preview, BorderLayout.CENTER);
248
249 lower_pane.setLayout(new GridLayout(2,1));
250 lower_pane.add(separations_pane);
251 lower_pane.add(button_pane);
252
253 button_pane.setLayout(new GridLayout(1,2));
254 button_pane.add(ok);
255 button_pane.add(cancel);
256
257 content_pane.setBorder(BorderFactory.createEmptyBorder(4,5,5,5));
258 content_pane.setLayout(new BorderLayout());
259 content_pane.add(upper_pane, BorderLayout.NORTH);
260 content_pane.add(center_pane, BorderLayout.CENTER);
261 content_pane.add(lower_pane, BorderLayout.SOUTH);
262 // Display
263 Dimension screen_size = Gatherer.config.screen_size;
264 controls.setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
265 screen_size = null;
266 if(show) {
267 controls.setVisible(true);
268 // Create the new metadata, hierarchy etc.
269 if(process) {
270 process((ElementWrapper)metadata.getSelectedItem(), getPreview(false));
271 return true;
272 }
273 return false;
274 }
275 else {
276 return true;
277 }
278 // We do everything -except- display the control.
279 }
280 else {
281 // we already have the controls
282 process = false;
283 controls.setVisible(true);
284 if(process) {
285 process((ElementWrapper)metadata.getSelectedItem(), getPreview(false));
286 return true;
287 }
288 return false;
289 }
290 }
291 /** Determine if this classifier and another given classifier are equal.
292 * @param object The classifier to check against for equality, as an <strong>Object</strong>.
293 * @see org.greenstone.gatherer.cdm.CustomClassifier
294 */
295 public boolean equals(Object object) {
296 if(object instanceof CustomClassifier) {
297 CustomClassifier classifier = (CustomClassifier) object;
298 if(getCommand().equalsIgnoreCase(classifier.getCommand())) {
299 //if (toString().equalsIgnoreCase(classifier.toString())) {
300 return true;
301 }
302 return false;
303 }
304 else {
305 if(getCommand().equalsIgnoreCase(object.toString())) {
306 return true;
307 }
308 return false;
309 }
310 }
311 /** Method to return this pseudo-classifier represented as a String.
312 * @return A <strong>String</strong>.
313 */
314 public String getCommand() {
315 StringBuffer text = new StringBuffer("classify ");
316 text.append("Hierarchy");
317 text.append(" -metadata ");
318 text.append(hidden_mde_name); // @TODO
319 if(buttonname_label.isSelected()) {
320 text.append(" -buttonname \"");
321 text.append(buttonname.getText());
322 text.append("\"");
323 }
324 text.append(" -hfile ");
325 text.append(hidden_mde_name + ".txt");
326 text.append(" -hlist_at_top");
327 text.append("\n");
328 return text.toString();
329 }
330 /** Retrieve the custom command, a command line that overrides and replaces some other 'actual' classifier.
331 * @param index The number of the classifer this one is replacing.
332 */
333 public String getCustomCommand(int index) {
334 StringBuffer text = new StringBuffer("customclassifier ");
335 text.append(NAME);
336 text.append(" -replaces CL");
337 text.append(index);
338 text.append(" -separations ");
339 text.append(getPreview(false));
340 return text.toString();
341 }
342 /** Get the name of this custom classifier.
343 * @return The name as a <strong>String</strong>.
344 */
345 public String getName() {
346 return NAME;
347 }
348 /** Process a record by adding hidden metadata as necessary.
349 * @param record The <strong>FileNode</strong> to be edited.
350 * @see org.greenstone.gatherer.msm.ElementWrapper
351 */
352 public void process(FileNode record) {
353 is_processing = true;
354 ElementWrapper element = (ElementWrapper) metadata.getSelectedItem();
355 if(element != null) {
356 addMetadata(0L, record, element);
357 }
358 is_processing = false;
359 }
360 /** Recreate a CustomAZList given several parameters including the real classifier created during custom design.
361 * @param classifier The real <strong>Classifier</strong>.
362 * @param separations A <strong>String</strong> representing the choosen separations.
363 * @see org.greenstone.gatherer.Gatherer
364 * @see org.greenstone.gatherer.cdm.Argument
365 * @see org.greenstone.gatherer.collection.CollectionManager
366 * @see org.greenstone.gatherer.msm.ElementWrapper
367 * @see org.greenstone.gatherer.msm.MetadataSetManager
368 * @see org.greenstone.gatherer.valuetree.GValueModel
369 * @see org.greenstone.gatherer.valuetree.GValueNode
370 */
371 public void recreate(Classifier classifier, String separations) {
372 // Rebuild controls
373 display(false);
374 // Calculate original element.
375 hidden_mde_name = classifier.getArgument("metadata").getValue();
376 ElementWrapper hidden_mde = gatherer.c_man.getCollection().msm.getElement(hidden_mde_name);
377 String mde_name = (hidden_mde_name.substring(MetadataSetManager.HIDDEN.length() + 1)).replace('_','.');
378 ElementWrapper mde = gatherer.c_man.getCollection().msm.getElement(mde_name);
379 // Set metadata element combobox.
380 metadata.setSelectedItem(mde);
381 // Set button name.
382 String bn = classifier.getArgument("buttonname").getValue();
383 if(bn != null) {
384 buttonname_label.setSelected(true);
385 buttonname.setBackground(Color.white);
386 buttonname.setEnabled(true);
387 buttonname.setText(bn);
388 }
389 // Set sort element.
390 String sort_mde_name = classifier.getArgument("sort").getValue();
391 ElementWrapper sort_mde = gatherer.c_man.getCollection().msm.getElement(sort_mde_name);
392 if(sort_mde != null) {
393 sort_label.setSelected(true);
394 sort.setBackground(Color.white);
395 sort.setEnabled(true);
396 sort.setSelectedItem(sort_mde);
397 }
398 // Recover value tree.
399 GValueModel model = gatherer.c_man.getCollection().msm.getValueTree(hidden_mde);
400 // For each token in the tokenizer, set toggle buttons and recover node.
401 StringTokenizer tokenizer = new StringTokenizer(separations, ",");
402 mappings = new Hashtable();
403 while(tokenizer.hasMoreTokens()) {
404 String key = tokenizer.nextToken();
405 for(int j = 1; j < STRING_VALUES.length; j++) {
406 if(key.startsWith(STRING_VALUES[0])) {
407 j = STRING_VALUES.length;
408 }
409 else if(key.startsWith(STRING_VALUES[j])) {
410 separators[j].setSelected(true);
411 j = STRING_VALUES.length;
412 }
413 }
414 // Rebuild mappings hashtable for this pattern. Add it if not present.
415 GValueNode node = model.addValue(key);
416 // If the key is simple, ie "C", then add the mapping "C"->node
417 if(node == null) {
418 // Do nothing.
419 }
420 else if(key.indexOf("-") == -1) {
421 mappings.put(key, node);
422 }
423 // If the key is more complex, say "D-L", then we add mappings for all string values so "D"->node, "E"->node, ... "L"->node. This is miles eaiser to do here, than when you are trying to match a records metadata ie matchin "D-L" to "Igloos are cool". Note that it is guarantee that the keys are all of the same length, ie "A-L", "MA-ML", "MMA-STF", "STFA-ZZZZ".
424 else {
425 String start = key.substring(0, key.indexOf("-"));
426 String end = key.substring(key.indexOf("-") + 1);
427 for(key = start; key != null && key.compareTo(end) <= 0; key = increment(key)) {
428 ///ystem.err.println("Adding " + key + " -> " + node);
429 mappings.put(key, node);
430 }
431 }
432 }
433 separators_preview.setText(getPreview(true));
434 }
435 /** Sets the value of Gatherer, for those classes loaded dynamically.
436 * @param gatherer A reference to the <strong>Gatherer</strong>.
437 */
438 public void setGatherer(Gatherer gatherer) {
439 this.gatherer = gatherer;
440 }
441
442 public void setManager(ClassifierManager manager) {
443 }
444
445 /** Translate this object into a string such as you would find in the collection configuration file.
446 * @return A <strong>String</strong> representation.
447 */
448 public String toString() {
449 StringBuffer text = new StringBuffer("classify ");
450 text.append(NAME);
451 text.append(" -metadata ");
452 text.append(metadata.getSelectedItem().toString());
453 if(buttonname_label.isSelected()) {
454 text.append(" -buttonname \"");
455 text.append(buttonname.getText());
456 text.append("\"");
457 }
458 text.append(" -separations ");
459 text.append(getPreview(false));
460 return text.toString();
461 }
462 /** Add a pertinant piece of metadata to the given record. */
463 private void addMetadata(long id, FileNode record, ElementWrapper element) {
464 // Add custom metadata based on target metadata. No recursion.
465 ArrayList temp_value = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile(), element);
466 boolean found = false; // Only want to add the custom classifier metadata once.
467 for(int i = 0; !found && i < temp_value.size(); i++) {
468 String metadata_value = (String)temp_value.get(i);
469 for(Enumeration keys = mappings.keys(); metadata_value != null && keys.hasMoreElements(); ) {
470 String key = (String)keys.nextElement();
471 // Try to match the value and the pattern. Remember to check for any case, ie key is '#' and metadata_value starts with anything other than a character.
472 if(metadata_value.toUpperCase().startsWith(key) || (key.equals("#") && !Character.isLetter(metadata_value.charAt(0)))) {
473 GValueNode node = (GValueNode) mappings.get(key);
474 if(node != null) { // And it shouldn't.
475 Metadata metadata = new Metadata(node);
476 //record.addMetadata(id, metadata, MetaEditPromptBox.ACCUMULATE_ALL, false, 1);
477 Gatherer.c_man.getCollection().msm.fireMetadataChanged(0, record, null, metadata);
478 found = true;
479 }
480 }
481 }
482 }
483 }
484 /** Create a separation preview String from the currently selected toggle buttons.
485 * @param spaces <i>true</i> if the returned String should contain spaces for readability, <i>false</i> if we mean to process it and spaces would just make that more difficult.
486 * @return A <strong>String</strong> representing the current separation state, such as "#, A-L, M-Z".
487 */
488 private String getPreview(boolean spaces) {
489 String sep_sep = ",";
490 if(spaces) {
491 sep_sep = ", ";
492 }
493 StringBuffer preview = new StringBuffer(get(VALUES[0]));
494 for(int i = 1; i < separators.length - 1; i++) {
495 if(separators[i].isSelected()) {
496 // Special case, where we specifically avoid #-#, A...
497 if(i == 1) {
498 preview.append(sep_sep);
499 preview.append(get(VALUES[i]));
500 }
501 // Check previous value. We don't want ...A-L, M-M, N...
502 else if(separators[i - 1].isSelected()) {
503 preview.append(sep_sep);
504 preview.append(get(VALUES[i]));
505 }
506 // Otherwise ..-VALUE[i-1], VALUE[i]...
507 else {
508 preview.append("-");
509 preview.append(get(VALUES[i-1]));
510 preview.append(sep_sep);
511 preview.append(get(VALUES[i]));
512 }
513 }
514 }
515 // Special case of "...-Y,Z"
516 if(separators[separators.length - 1].isSelected()) {
517 preview.append("-");
518 preview.append(get(VALUES[VALUES.length - 2]));
519 preview.append(sep_sep);
520 preview.append(get(VALUES[VALUES.length - 1]));
521 }
522 // Otherwise "...-Z"
523 else {
524 preview.append("-");
525 preview.append(get(VALUES[VALUES.length - 1]));
526 }
527 return preview.toString();
528 }
529 /** Load the custom argument dictionary if necessary, then retrieve the requested phrase.
530 * @param key A key <strong>String</strong> which maps to the phrase to retrieve.
531 * @return A phrase as a <strong>String</strong>.
532 * @see org.greenstone.gatherer.Dictionary
533 * @see org.greenstone.gatherer.Gatherer
534 */
535 private String get(String key) {
536 if(dictionary == null) {
537 dictionary = ResourceBundle.getBundle("org.greenstone.gatherer.cdm.custom." + NAME, Gatherer.config.getLocale("general.locale", false));
538 }
539 return dictionary.getString(key);
540 }
541 /** Listens to the buttonname checkbox, and enable buttonname appropriately when action detected. */
542 private class ButtonNameListener
543 implements ActionListener {
544 /** When an action is performed on a registered control, this method is called which either enables or disables the buttonname field depending on the buttonname checkbox.
545 * @param event An <strong>ActionEvent</strong> containing information about the action performed.
546 */
547 public void actionPerformed(ActionEvent event) {
548 if(buttonname_label.isSelected()) {
549 buttonname.setBackground(Color.white);
550 buttonname.setEnabled(true);
551 }
552 else {
553 buttonname.setBackground(Color.lightGray);
554 buttonname.setEnabled(false);
555 }
556 }
557 }
558 /** Listens to the cancel button, and disposes appropriately when action detected. */
559 private class CancelListener
560 implements ActionListener {
561 /** If the cancel button is pressed then we should dispose of the dialog without processing any records.
562 * @param event An <strong>ActionEvent</strong> containing information about the action performed.
563 */
564 public void actionPerformed(ActionEvent event) {
565 process = false;
566
567 controls.setVisible(false);
568 }
569 }
570 /** Listens to the ok button, and sets process to true then disposes appropriately when action detected. */
571 private class OKListener
572 implements ActionListener {
573 /** If the ok button is pressed then we should dispose of the dialog after processing all records.
574 * @param event An <strong>ActionEvent</strong> containing information about the action performed.
575 */
576 public void actionPerformed(ActionEvent event) {
577 process = true;
578 controls.setVisible(false);
579 }
580 }
581 /** Listens for the toggling of a separator value, and updates the separator preview field. */
582 private class SeparatorListener
583 implements ActionListener {
584 /** When one of the separator buttons is toggled we update the separator preview field.
585 * @param event An <strong>ActionEvent</strong> containing information about the action performed.
586 */
587 public void actionPerformed(ActionEvent event) {
588 separators_preview.setText(getPreview(true));
589 }
590 }
591 /** Listens to the sort checkbox, and enables/disables sort appropriately when action detected. */
592 private class SortListener
593 implements ActionListener {
594 /** If this checkbox is actioned, enabled or disable the sort control based on whether we are checked (selected) or not.
595 * @param event An <strong>ActionEvent</strong> containing information about the action performed.
596 */
597 public void actionPerformed(ActionEvent event) {
598 if(sort_label.isSelected()) {
599 sort.setBackground(Color.white);
600 sort.setEnabled(true);
601 }
602 else {
603 sort.setBackground(Color.lightGray);
604 sort.setEnabled(false);
605 }
606 }
607 }
608 /** Display a progress bar while creating the required hidden system hierarchy.
609 * @param element An <strong>ElementWrapper</strong> containing the metadata element we want to build a custom classifier on.
610 * @param state A <strong>String</strong> representing the currently selected separators, of the form "#,A-L,MA-ML,MM-MZ,N-Z".
611 */
612 private void process(ElementWrapper element, String state) {
613 // Create a new progress bar dialog, using these divisions:
614 // 33% - Creation and removal of old metadata tree.
615 // 33% - Creation of new metadata tree, 33 / <number of separations>.
616 // 33% - Allocation of metadata to records, 33 / <number of files>.
617 // Hopefully the previous dialog should have already vanished (its dispose() caused by the process click).
618 is_processing = true;
619 pd = new ProgressDialog("Custom Classifier Creation", "Preparing for operation.");
620 // Step 1: Create the new dummy element and add it if necessary.
621 pd.setText("Creating dummy metadata element.");
622 ///ystem.out.println("Creating dummy metadata element.");
623 MetadataSet hidden_mds = gatherer.c_man.getCollection().msm.getSet(MetadataSetManager.HIDDEN);
624 Element hidden_e = hidden_mds.getElement(element.toString().replace('.','_'));
625 boolean found = true;
626 ElementWrapper hidden_mde = null;
627 if(hidden_mde != null) {
628 hidden_mde = new ElementWrapper(hidden_e);
629 }
630 else {
631 String language_code = Gatherer.config.interface_language.getCode();
632 hidden_mde = hidden_mds.addElement(element.toString().replace('.','_'), language_code);
633 found = false;
634 }
635 hidden_mde_name = hidden_mde.toString();
636 pd.increase(1,2,20);
637 // Step 2: Remove any existing value tree for this element. Wastefull I know but I don't want to lag for too long, and this is the quickest way.
638 pd.setText("Removing any existing hierarchy.");
639 ///ystem.out.println("Removing any existing hierarchy.");
640 if(found) {
641 hidden_mds.removeValueTree(hidden_mde);
642 }
643 pd.increase(1,2,20);
644 // Step 3: Create a value tree for this element.
645 pd.setText("Creating new hierary root.");
646 ///ystem.out.println("Creating new hierary root.");
647 GValueModel value_tree = gatherer.c_man.getCollection().msm.getValueTree(hidden_mde);
648 pd.increase(5, 5, 5);
649 // Step 4: Build the single level value hierarchy using state's value. Here a hashtable mapping string patterns to value nodes is built.
650 pd.setText("Creating separations.");
651 ///ystem.out.println("Creating separations.");
652 StringTokenizer tokenizer = new StringTokenizer(state, ",");
653 int separator_count = tokenizer.countTokens();
654 mappings = new Hashtable();
655 for(int i = 0; i < separator_count; i++) {
656 String key = tokenizer.nextToken();
657 GValueNode node = value_tree.addValue(key);
658 // If the key is simple, ie "C", then add the mapping "C"->node
659 if(key.indexOf("-") == -1) {
660 mappings.put(key, node);
661 }
662 // If the key is more complex, say "D-L", then we add mappings for all string values so "D"->node, "E"->node, ... "L"->node. This is miles eaiser to do here, than when you are trying to match a records metadata ie matchin "D-L" to "Igloos are cool". Note that it is guarantee that the keys are all of the same length, ie "A-L", "MA-ML", "MMA-STF", "STFA-ZZZZ".
663 else {
664 String start = key.substring(0, key.indexOf("-"));
665 String end = key.substring(key.indexOf("-") + 1);
666 for(key = start; key != null && key.compareTo(end) <= 0; key = increment(key)) {
667 mappings.put(key, node);
668 }
669 }
670 // Each iteration.
671 pd.increase(1, separator_count, 25);
672 }
673 // Step 5: Recurse the entire tree, adding the appropriate values where necessary (overwriting any existing value for this element).
674 pd.setText("Allocating metadata to records.");
675 ///ystem.out.println("Allocating metadata to records.");
676 int count = gatherer.c_man.getCollection().getCount();
677 TreeModel record_tree = gatherer.c_man.getRecordSet();
678 FileNode records[] = ArrayTools.add(null, (FileNode)record_tree.getRoot());
679 while(records != null) {
680 FileNode current_record = records[0];
681 // There is no point in assigning hidden metadata to a file node we doesn't have any of that metadata set...
682 ArrayList current_metadatum = Gatherer.c_man.getCollection().gdm.getMetadata(current_record.getFile());
683 for(int z = 0; z < current_metadatum.size(); z++) {
684 Metadata current_metadata = (Metadata) current_metadatum.get(z);
685 if(current_metadata.getElement().equals(element) && current_metadata.isFileLevel()) {
686 ///ystem.err.println("The file " + current_record + " has previous " + element + " metadata in: " + current_metadata);
687 addMetadata(0L, current_record, element);
688 }
689 }
690 if(!current_record.isLeaf()) {
691 ///ystem.out.println("Add all children to records at once hopefully.");
692 // Add all children to records at once hopefully.
693 FileNode children[] = new FileNode[current_record.getChildCount()];
694 for(int i = 0; i < children.length; i++) {
695 children[i] = (FileNode) current_record.getChildAt(i);
696 }
697 records = ArrayTools.add(records, children);
698 }
699 records = ArrayTools.tail(records);
700 pd.increase(1, count, 50);
701 }
702 is_processing = false;
703 // Step 6: Done.
704 pd.dispose();
705 pd = null;
706 }
707
708 public boolean isProcessing() {
709 return is_processing;
710 }
711
712 /** A dialog window used to show the progress of processing the file records in terms of adding hidden metadata. */
713 private class ProgressDialog
714 extends JDialog {
715 /** The current value represented by the progress bar (where value is double and the progress bar shows an int). */
716 private double value = 0.0;
717 /** The default size of this dialog. */
718 private Dimension dialog_size = new Dimension(500,100);
719 /** The label explaining what action is currently happening (1 of 5 steps). */
720 private JLabel label = new JLabel();
721 /** The progress bar itself. */
722 private JProgressBar bar = new JProgressBar();
723 /** Constructor.
724 * @param t The title for this dialog as a <strong>String</strong>.
725 * @param l The initial label for the progress bar also a <strong>String</strong>.
726 */
727 public ProgressDialog(String t, String l) {
728 super();
729 setModal(false);
730 setSize(dialog_size);
731 setTitle(t);
732 // Create
733 JPanel content_pane = (JPanel) getContentPane();
734 label.setText(l);
735 bar.setMaximum(100);
736 bar.setMinimum(0);
737 bar.setValue(0);
738 // Connect
739 // Layout
740 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
741 content_pane.setLayout(new BorderLayout());
742 content_pane.add(label, BorderLayout.NORTH);
743 content_pane.add(bar, BorderLayout.CENTER);
744 // Display
745 Dimension screen_size = Gatherer.config.screen_size;
746 setLocation((screen_size.width - dialog_size.width) / 2, (screen_size.height - dialog_size.height) / 2);
747 screen_size = null;
748 show();
749 }
750 /** Increase the progress bar by a certain ammount.
751 * @param item An <i>int</i> indicating what item number this is.
752 * @param out_of An <i>int</i> detailing the number of items expected in total for this phase.
753 * @param total The maximum value of the progress bar as an <i>int</i>.
754 */
755 public void increase(int item, int out_of, int total) {
756 value = value + (((double) item / (double) out_of) * (double)total);
757 int int_value = (int) value;
758 ///ystem.err.println("Value is " + value + " which rounds to " + int_value);
759 if(int_value != bar.getValue()) {
760 bar.setValue(int_value);
761 }
762 }
763 /** Set the message shown in the progress bar.
764 * @param l The new progress bar label as a <strong>String</strong>.
765 */
766 public void setText(String l) {
767 label.setText(l);
768 }
769 }
770 /** The String equivelent of int++, this method increments the value of a String by one letter at a time.
771 * @param str The <strong>String</strong> to be incremented.
772 * @return A <strong>String</strong> whose 'checksum' value (adding the INT_VALUE[] of the letters together) is one greater than the initial String, while still containing only those values in STRING_VALUE[].
773 */
774 public String increment(String str) {
775 ///ystem.err.print("Incrementing " + str + " -> ");
776 char chars[] = str.toCharArray();
777 boolean found = false;
778 int pos = str.length() - 1;
779 while(found == false && pos >= 0) {
780 if((int)chars[pos] + 1 <= INT_VALUES[26]) {
781 if((int)chars[pos] == INT_VALUES[0]) { // Anything else symbol
782 chars[pos] = (char) INT_VALUES[1];
783 }
784 else {
785 chars[pos] = (char)((int)chars[pos] + 1);
786 }
787 found = true;
788 }
789 else {
790 chars[pos] = (char)INT_VALUES[1];
791 pos--;
792 }
793 }
794 // If we haven't found it yet, return null.
795 if(!found) {
796 return null;
797 }
798 ///ystem.err.println(String.valueOf(chars));
799 return String.valueOf(chars);
800 }
801}
802
803
804
805
806
Note: See TracBrowser for help on using the repository browser.