source: trunk/gli/src/org/greenstone/gatherer/cdm/FormatManager.java@ 4511

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

2030109: FormatManager will now try to parse more lines from the collect.cfg if it doesn't have enough arguments (3) for a format command.

  • Property svn:keywords set to Author Date Id Revision
File size: 32.8 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 */
37
38
39
40
41
42
43/* GPL_HEADER */
44package org.greenstone.gatherer.cdm;
45/**************************************************************************************
46 * Title: Gatherer
47 * Description: The Gatherer: a tool for gathering and enriching a digital collection.
48 * Company: The University of Waikato
49 * Written: /05/02
50 * Revised: 04/10/02 - Commented
51 **************************************************************************************/
52import java.awt.BorderLayout;
53import java.awt.CardLayout;
54import java.awt.Color;
55import java.awt.Dimension;
56import java.awt.GridLayout;
57import java.awt.event.ActionEvent;
58import java.awt.event.ActionListener;
59import java.awt.event.KeyAdapter;
60import java.awt.event.KeyEvent;
61import java.util.ArrayList;
62import java.util.Collections;
63import java.util.Vector;
64import javax.swing.BorderFactory;
65import javax.swing.BoxLayout;
66import javax.swing.ButtonGroup;
67import javax.swing.DefaultComboBoxModel;
68import javax.swing.JButton;
69import javax.swing.JCheckBox;
70import javax.swing.JComboBox;
71import javax.swing.JLabel;
72import javax.swing.JList;
73import javax.swing.JPanel;
74import javax.swing.JScrollPane;
75import javax.swing.JTabbedPane;
76import javax.swing.JTextArea;
77import javax.swing.JTextField;
78import javax.swing.JToggleButton;
79import javax.swing.ListSelectionModel;
80import javax.swing.event.ListSelectionEvent;
81import javax.swing.event.ListSelectionListener;
82import org.greenstone.gatherer.Gatherer;
83import org.greenstone.gatherer.cdm.Classifier;
84import org.greenstone.gatherer.cdm.ClassifierManager;
85import org.greenstone.gatherer.cdm.CollectionDesignManager;
86import org.greenstone.gatherer.cdm.CommandTokenizer;
87import org.greenstone.gatherer.cdm.DynamicListModel;
88import org.greenstone.gatherer.cdm.Format;
89import org.greenstone.gatherer.msm.ElementWrapper;
90import org.greenstone.gatherer.util.Utility;
91/** This class maintains a list of format statements, and allows the addition and removal of these statements.
92 * @author John Thompson, Greenstone Digital Library, University of Waikato
93 * @version 2.3
94 */
95public class FormatManager
96 extends DynamicListModel {
97 /** The main manager so we can get to ClassifierManager which is needed to turn position reference such as "CL1" into the appropriate Classifier. */
98 private CollectionDesignManager manager = null;
99 /** The controls used to edit the format commands. */
100 private Control controls = null;
101 /** A reference to ourselves so inner classes can get at the model. */
102 private DynamicListModel model = null;
103 /** A reference to the Gatherer. */
104 private Gatherer gatherer = null;
105 /** We may have somehow recieved a format command which references a classifier that hasn't been parsed yet, so this variable holds a list of failed commands which are retried after the loading is complete. */
106 private Vector unresolved_commands = null;
107 /** Constructor.
108 * @param gatherer A reference to the <strong>Gatherer</strong>.
109 * @param manager A reference to the <strong>CollectionDesignManager</strong> that created this manager.
110 */
111 public FormatManager(Gatherer gatherer, CollectionDesignManager manager) {
112 super();
113 this.gatherer = gatherer;
114 this.manager = manager;
115 this.model = this;
116 this.unresolved_commands = new Vector();
117 }
118 /** Method to add a new format to this manager.
119 * @param format The <strong>Format</strong> to add.
120 */
121 public void addFormat(Format format) {
122 if(formatExists(format) == null) {
123 addElement(format);
124 gatherer.c_man.configurationChanged();
125 }
126 }
127 /** Method which determines if a certain format exists, in which case it must be removed before a new format of the same component is added. For general formatting statements this compares types, for 'custom' ones it compares list and part.
128 * @param format The <strong>Format</strong> whose uniqueness we want to check.
129 * @return The <strong>Format</strong> that matches the one given, or <i>null</i> if no such format exists.
130 */
131 public Format formatExists(Format format) {
132 for(int i = 0; i < size(); i++) {
133 Format current = (Format) get(i);
134 if(format.getPosition().equals(current.getPosition())) {
135 return current;
136 }
137 }
138 return null;
139 }
140 /** Gets the format indicated by the index.
141 * @param index The location of the desired format, as an <i>int</i>.
142 */
143 public Format getFormat(int index) {
144 return (Format)get(index);
145 }
146 /** Method to retrieve this managers controls.
147 * @return The <strong>Control</strong> for this collection.
148 */
149 public Control getControls() {
150 if(controls == null) {
151 controls = new Control();
152 }
153 return controls;
154 }
155 /** Method to invalidate controls when some significant change has occured within the collection. */
156 public void invalidateControls() {
157 if(controls != null) {
158 controls.destroy();
159 }
160 controls = null;
161 }
162 /** This method attempts to parse a format command from the given string. If a format is parsed, it is immediately added to this managers list of format commands.
163 * @param command The <strong>String</strong> we are trying to parse a command from.
164 * @param finished A <i>boolean</i> which is <i>true</i> if we are calling this after the the input has been completely parsed (i.e all legal Classifiers are know to exist), or <i>false</i> otherwise.
165 * @return A <i>boolean</i> which is <i>true</i> if we managed to parse a command, <i>false</i> otherwise.
166 * @see org.greenstone.gatherer.cdm.Classifier
167 * @see org.greenstone.gatherer.cdm.CommandTokenizer
168 * @see org.greenstone.gatherer.cdm.Format
169 */
170 public boolean parse(String command, boolean finished) {
171 String temp = command.toLowerCase();
172 if(temp.startsWith("format")) {
173 if(finished) {
174 CommandTokenizer ct = new CommandTokenizer(command);
175 ct.nextToken(); // Throw away 'format'
176 String position = ct.nextToken();
177 String value = ct.nextToken();
178 // String speech marks.
179 if(value.startsWith("\"") && value.endsWith("\"")) {
180 if(value.equals("\"\"")) {
181 value = "";
182 }
183 else {
184 value = value.substring(1, value.length() - 1);
185 }
186 }
187 // Ensure we parsed a good command.
188 if(position != null && value != null) {
189 // The trickiest bit of parsing format commands is figuring out how much of the position is feature, and how much part. Since the parts are far less likely to change or be customized in any way, we'll try to match them first (except "" of course).
190 String feature_name = null;
191 String part = null;
192 // If this works, then we're all finished. Yay.
193 for(int i = 1; i < Format.DEFAULT_PARTS.length; i++) {
194 if(position.endsWith(Format.DEFAULT_PARTS[i])) {
195 part = Format.DEFAULT_PARTS[i];
196 feature_name = position.substring(0, position.length() - part.length());
197 }
198 }
199 // Otherwise we can attempt to find the default features, but we have less chance of success.
200 if(feature_name == null || part == null) {
201 for(int i = 1; i < Format.DEFAULT_FEATURES.length; i++) {
202 if(position.startsWith(Format.DEFAULT_FEATURES[i])) {
203 feature_name = Format.DEFAULT_FEATURES[i];
204 if(position.length() > feature_name.length()) {
205 part = position.substring(feature_name.length());
206 }
207 else {
208 part = "";
209 }
210 }
211 }
212 }
213 // Otherwise we can assume we are dealing with a classifier and split the position using...
214 // position ::= <classifier_position><part>
215 // classifier_position ::= <alphanum>[0-9]$
216 // part ::= ^![0-9]<alpha>
217 // But I don't like my chances of this working if someone does a scary like CL4Part8B. I just have
218 // to hope no-one uses numbers, and no-one expects CustomFeatureCustomPart to parse properly.
219 if(feature_name == null || part == null) {
220 part = "";
221 boolean found = false;
222 int index = position.length() - 1;
223 while(index >= 0 && !found) {
224 if(Character.isDigit(position.charAt(index))) {
225 found = true;
226 }
227 else {
228 part = position.charAt(index) + part;
229 index--;
230 }
231 }
232 if(found) {
233 feature_name = position.substring(0, index + 1);
234 }
235 // We ran out of string. No digits. Arg!
236 else {
237 part = null;
238 }
239 }
240 // And if all else fails, stick it all in feature.
241 if(feature_name == null || part == null) {
242 feature_name = position;
243 part = "";
244 }
245 // Now try to retrieve a classifier with the feature name.
246 Object feature = null;
247 String feature_name_lc = feature_name.toLowerCase();
248 if(feature_name_lc.startsWith("cl") && feature_name.length() >= 3) {
249 String raw_index = feature_name.substring(2); // Lose the 'CL'
250 int index = -1;
251 try {
252 index = Integer.parseInt(raw_index);
253 }
254 catch(NumberFormatException nfe) {
255 nfe.printStackTrace();
256 }
257 feature = manager.classifiers.getClassifier(index - 1);
258 }
259 else {
260 ///ystem.err.println("name to short for classifier.");
261 }
262 if(feature == null) {
263 feature = feature_name;
264 }
265 ///ystem.err.println("Feature name = " + feature_name + "\nPart = " + part);
266 if(feature instanceof Classifier) {
267 ///ystem.err.println("Feature is a classifier.");
268 }
269 else {
270 ///ystem.err.println("Feature is a String.");
271 }
272 // Now we have a quick look at value. If its true or false we create a FLAG type
273 if(value.equalsIgnoreCase("true")) {
274 addFormat(new Format(feature, part, true));
275 }
276 else if(value.equalsIgnoreCase("false")) {
277 addFormat(new Format(feature, part, false));
278 }
279 // Otherwise add a plain old value based format
280 else {
281 addFormat(new Format(feature, part, value));
282 }
283 return true;
284 }
285 // Somethings gone terribly, terribly wrong.
286 return false;
287 }
288 else {
289 // Ensure we have enough tokens for a format command
290 CommandTokenizer ct = new CommandTokenizer(command);
291 while(ct.countTokens() < 3) {
292 command = manager.parseMore(command);
293 ct = new CommandTokenizer(command);
294 }
295 unresolved_commands.add(command);
296 }
297 return true;
298 }
299 return false;
300 }
301 /** Method to remove a format.
302 * @param format The <strong>Format</strong> to remove.
303 */
304 public void removeFormat(Format format) {
305 removeElement(format);
306 gatherer.c_man.configurationChanged();
307 }
308 /** Method which attempts to reparse obvious format commands which previously referenced unresovable Classifiers.
309 */
310 public void reparseUnresolved() {
311 for(int i = 0; i < unresolved_commands.size(); i++) {
312 if(!parse((String)unresolved_commands.get(i), true)) {
313 ///ystem.err.println("*** Error: Command " + unresolved_commands.get(i));
314 }
315 }
316 // Regardless of if they work, clear the commands.
317 unresolved_commands.clear();
318 }
319 /** Method to produce a block of text representing the format commands in this manager, ready to be used in the collection configuration file.
320 * @return A <strong>String</strong> containing a series of format commands.
321 */
322 public String toString() {
323 StringBuffer text = new StringBuffer("");
324 for(int i = 0; i < size(); i++) {
325 Format format = (Format) get(i);
326 text.append(format.toString());
327 text.append("\n");
328 }
329 text.append("\n");
330 return text.toString();
331 }
332 /** Overloaded to call get with both a key and an empty argument array.
333 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
334 * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call.
335 */
336 private String get(String key) {
337 return get(key, null);
338 }
339 /** Used to retrieve a property value from the Locale specific ResourceBundle, based upon the key and arguments supplied. If the key cannot be found or if some other part of the call fails a default (English) error message is returned. <BR>
340 * Here the get recieves a second argument which is an array of Strings used to populate argument fields, denoted {<I>n</I>}, within the value String returned. Note that argument numbers greater than or equal to 32 are automatically mapped to the formatting String named Farg<I>n</I>.
341 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
342 * @param args A <strong>String[]</strong> used to populate argument fields within the complete String.
343 * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call.
344 * @see org.greenstone.gatherer.Gatherer
345 * @see org.greenstone.gatherer.Dictionary
346 */
347 private String get(String key, String args[]) {
348 if(key.indexOf('.') == -1) {
349 key = "CDM.FormatManager." + key;
350 }
351 return gatherer.dictionary.get(key, args);
352 }
353 private class Control
354 extends JPanel {
355 /** Do we ignore selection changing events (mainly because we're generating them!) */
356 private boolean ignore = false;
357 private boolean new_entry = true;
358 private boolean ready = false;
359 private ButtonGroup button_group = null;
360 private CardLayout card_layout = null;
361 private Dimension LABEL_SIZE = new Dimension(175,25);
362 private Format current_format = null;
363 private String view_type = "custom";
364 private JButton add = null;
365 private JButton insert = null;
366 private JButton preview = null;
367 private JButton remove = null;
368 private JComboBox feature = null;
369 private JComboBox part = null;
370 private JComboBox special = null;
371 private JLabel editor_label = null;
372 private JLabel feature_label = null;
373 private JLabel format_list_label = null;
374 private JLabel part_label = null;
375 private JLabel special_label = null;
376 private JLabel title = null;
377 private JLabel value_label = null;
378 private JList format_list = null;
379 private JPanel blank_pane = null;
380 private JPanel control_pane = null;
381 private JPanel editor_pane = null;
382 private JPanel feature_pane = null;
383 private JPanel format_list_pane = null;
384 private JPanel header_pane = null;
385 private JPanel inner_button_pane = null;
386 private JPanel inner_state_pane = null;
387 private JPanel inner_value_pane = null;
388 private JPanel options_pane = null;
389 private JPanel outer_button_pane = null;
390 private JPanel part_pane = null;
391 private JPanel special_pane = null;
392 private JPanel state_pane = null;
393 private JPanel value_pane = null;
394 private JPanel view_pane = null;
395 private JTextArea editor = null;
396 private JTextArea instructions = null;
397 private JTextField value = null;
398 private JToggleButton off = null;
399 private JToggleButton on = null;
400 private String BLANK = "blank";
401 private String CUSTOM = "custom";
402 private String FLAG = "flag";
403 private String PARAM = "param";
404 private Vector part_model = null;
405 private Vector special_model = null;
406 public Control() {
407 ArrayList feature_model = new ArrayList();
408 // Add the set options
409 for(int i = 0; i < Format.DEFAULT_FEATURES.length; i++) {
410 feature_model.add(new Entry(Format.DEFAULT_FEATURES[i]));
411 }
412 // Now the classifiers.
413 for(int j = 0; j < manager.classifiers.size(); j++) {
414 feature_model.add(new Entry(manager.classifiers.getClassifier(j)));
415 }
416 Collections.sort(feature_model);
417 part_model = new Vector();
418 part_model.add("");//get("Custom"));
419 part_model.add("DateList");
420 part_model.add("HList");
421 part_model.add("Invisible");
422 part_model.add("VList");
423 special_model = new Vector();
424 special_model.add("[Text]");
425 special_model.add("[link]");
426 special_model.add("[/link]");
427 special_model.add("[icon]");
428 special_model.add("[num]");
429 special_model.add("[parent():_]");
430 special_model.add("[parent(Top):_]");
431 special_model.add("[parent(All'_'):_]");
432 Vector elements = gatherer.c_man.msm.getAssignedElements();
433 for(int i = 0; i < elements.size(); i++) {
434 special_model.add("[" + ((ElementWrapper)elements.get(i)).toString() + "]");
435 }
436 Collections.sort(special_model);
437 // Create
438 add = new JButton(get("Add"));
439 add.setEnabled(false);
440 blank_pane = new JPanel();
441 button_group = new ButtonGroup();
442 card_layout = new CardLayout();
443 control_pane = new JPanel();
444 editor = new JTextArea();
445 editor.setCaretPosition(0);
446 editor.setLineWrap(true);
447 editor.setWrapStyleWord(true);
448 editor_label = new JLabel(get("Editor"));
449 editor_label.setHorizontalAlignment(JLabel.CENTER);
450 editor_pane = new JPanel();
451 feature = new JComboBox(feature_model.toArray());
452 feature.setEditable(true);
453 feature_label = new JLabel(get("Feature"));
454 feature_label.setPreferredSize(LABEL_SIZE);
455 format_list = new JList(model);
456 format_list_label = new JLabel(get("Assigned_Formats"));
457 format_list_pane = new JPanel();
458 feature_pane = new JPanel();
459 header_pane = new JPanel();
460 inner_button_pane = new JPanel();
461 inner_state_pane = new JPanel();
462 inner_value_pane = new JPanel();
463 insert = new JButton(get("Insert"));
464 instructions = new JTextArea(get("Instructions"));
465 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
466 instructions.setEditable(false);
467 instructions.setLineWrap(true);
468 instructions.setRows(4);
469 instructions.setWrapStyleWord(true);
470 off = new JToggleButton(get("Off"));
471 off.setSelected(false);
472 on = new JToggleButton(get("On"));
473 on.setSelected(true);
474 options_pane = new JPanel();
475 outer_button_pane = new JPanel();
476 part = new JComboBox(part_model);
477 part.setEditable(true);
478 part_label = new JLabel(get("Part"));
479 part_label.setPreferredSize(LABEL_SIZE);
480 part_pane = new JPanel();
481 preview = new JButton(get("Preview"));
482 preview.setEnabled(false);
483 remove = new JButton(get("Remove"));
484 remove.setEnabled(false);
485 special = new JComboBox(special_model);
486 special_label = new JLabel(get("Special"));
487 special_label.setHorizontalAlignment(JLabel.CENTER);
488 special_pane = new JPanel();
489 state_pane = new JPanel();
490 title = new JLabel(get("Title"));
491 title.setHorizontalAlignment(JLabel.CENTER);
492 title.setOpaque(true);
493 value = new JTextField();
494 value_label = new JLabel(get("Value"));
495 value_pane = new JPanel();
496 view_pane = new JPanel();
497 // Connect
498 add.addActionListener(new AddListener());
499 button_group.add(on);
500 button_group.add(off);
501 editor.addKeyListener(new EditorListener());
502 feature.addActionListener(new FeatureListener());
503 format_list.addListSelectionListener(new FormatListListener());
504 insert.addActionListener(new InsertListener());
505 off.addActionListener(new StateListener());
506 on.addActionListener(new StateListener());
507 part.addActionListener(new PartListener());
508 preview.addActionListener(new PreviewListener());
509 remove.addActionListener(new RemoveListener());
510 value.addKeyListener(new ValueListener());
511 // Layout
512 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5));
513
514 header_pane.setLayout(new BorderLayout());
515 header_pane.add(title, BorderLayout.NORTH);
516 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER);
517
518 format_list_label.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
519
520 format_list_pane.setLayout(new BorderLayout());
521 format_list_pane.add(format_list_label, BorderLayout.NORTH);
522 format_list_pane.add(new JScrollPane(format_list), BorderLayout.CENTER);
523
524 feature_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
525
526 feature_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
527 feature_pane.setLayout(new BorderLayout());
528 feature_pane.add(feature_label, BorderLayout.WEST);
529 feature_pane.add(feature, BorderLayout.CENTER);
530
531 part_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
532
533 part_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
534 part_pane.setLayout(new BorderLayout());
535 part_pane.add(part_label, BorderLayout.WEST);
536 part_pane.add(part, BorderLayout.CENTER);
537
538 options_pane.setLayout(new GridLayout(2,1));
539 options_pane.add(feature_pane);
540 options_pane.add(part_pane);
541
542 inner_state_pane = new JPanel(new GridLayout(1,2));
543 inner_state_pane.add(on);
544 inner_state_pane.add(off);
545
546 state_pane.setLayout(new BorderLayout());
547 state_pane.add(inner_state_pane, BorderLayout.NORTH);
548 state_pane.add(new JPanel(), BorderLayout.CENTER);
549
550 value_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
551
552 inner_value_pane.setLayout(new BorderLayout());
553 inner_value_pane.add(value_label, BorderLayout.WEST);
554 inner_value_pane.add(value, BorderLayout.CENTER);
555
556 value_pane.setLayout(new BorderLayout());
557 value_pane.add(inner_value_pane, BorderLayout.NORTH);
558 value_pane.add(new JPanel(), BorderLayout.CENTER);
559
560 special_pane.setLayout(new GridLayout(3,1));
561 special_pane.add(special_label);
562 special_pane.add(special);
563 special_pane.add(insert);
564
565 editor_pane.setLayout(new BorderLayout());
566 editor_pane.add(editor_label, BorderLayout.NORTH);
567 editor_pane.add(new JScrollPane(editor), BorderLayout.CENTER);
568 editor_pane.add(special_pane, BorderLayout.EAST);
569
570 // Magic for view_pane card layout.
571 view_pane.setLayout(card_layout);
572 view_pane.add(editor_pane, CUSTOM);
573 view_pane.add(state_pane, FLAG);
574 //view_pane.add(value_pane, PARAM);
575
576 inner_button_pane.setLayout(new GridLayout(1,2));
577 inner_button_pane.add(add);
578 inner_button_pane.add(remove);
579
580 outer_button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
581 outer_button_pane.setLayout(new GridLayout(1,1));
582 outer_button_pane.add(inner_button_pane);
583 //outer_button_pane.add(preview);
584
585 control_pane.setLayout(new BorderLayout());
586 control_pane.add(options_pane, BorderLayout.NORTH);
587 control_pane.add(view_pane, BorderLayout.CENTER);
588 control_pane.add(outer_button_pane, BorderLayout.SOUTH);
589
590 setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
591 setLayout(new BorderLayout());
592 add(header_pane, BorderLayout.NORTH);
593 add(format_list_pane, BorderLayout.CENTER);
594 add(control_pane, BorderLayout.SOUTH);
595 ready = true;
596 }
597 public void destroy() {
598 }
599 /** Overriden to ensure that the instructions pane is scrolled to the top.
600 */
601 public void updateUI() {
602 if(ready) {
603 // Rebuild feature model.
604 ArrayList feature_model = new ArrayList();
605 // Add the set options
606 for(int i = 0; i < Format.DEFAULT_FEATURES.length; i++) {
607 feature_model.add(new Entry(Format.DEFAULT_FEATURES[i]));
608 }
609 // Now the classifiers.
610 for(int j = 0; j < manager.classifiers.size(); j++) {
611 feature_model.add(new Entry(manager.classifiers.getClassifier(j)));
612 }
613 feature.setModel(new DefaultComboBoxModel(feature_model.toArray()));
614 if(instructions != null) {
615 instructions.setCaretPosition(0);
616 }
617 }
618 super.updateUI();
619 }
620
621
622
623 /** Formats the formatting string so that it contains safe characters and isn't enclosed in speech marks etc. (Ironic eh?)
624 * @see java.lang.StringBuffer
625 */
626 private String format(String raw) {
627 String safe = null;
628 if(raw != null && raw.length() > 0) {
629 StringBuffer temp = new StringBuffer(raw);
630 // Remove quotes at start and end. Look at my wiggly save three lines of code skills.
631 char start = ' ';
632 while(temp.length() > 0 && (start = temp.charAt(0)) == '\"' || start == '\'') {
633 temp.delete(0, 1);
634 }
635 int length = 0;
636 char end = ' ';
637 while((length = temp.length()) > 0 && (end = temp.charAt(length - 1)) == '\"' || end == '\'') {
638 temp.delete(length - 1, length);
639 }
640 // Now escape quotes within the format string
641 int quote_index = -1;
642 while((quote_index = temp.indexOf("\"", quote_index + 1)) != -1) {
643 temp.replace(quote_index, quote_index + 1, "\\\"");
644 quote_index = quote_index + 1;
645 }
646 // Done.
647 safe = temp.toString();
648 }
649 return safe;
650 }
651 /** Remove safe characters from string replacing them with unsafe ones.
652 * @see java.lang.StringBuffer
653 */
654 private String unformat(String safe) {
655 String raw = null;
656 if(safe != null && safe.length() > 0) {
657 StringBuffer temp = new StringBuffer(safe);
658 int quote_index = -1;
659 while((quote_index = temp.indexOf("\\\"")) != -1) {
660 temp.replace(quote_index, quote_index + 2, "\"");
661 }
662 raw = temp.toString();
663 }
664 return raw;
665 }
666 /** Listens for clicks on the add button, and if the relevant details are provided adds a new format. */
667 private class AddListener
668 implements ActionListener {
669 public void actionPerformed(ActionEvent event) {
670 ignore = true;
671 Entry entry = (Entry)feature.getSelectedItem();
672 Object f = entry.getFeature();
673 String p = (String)part.getSelectedItem();
674 if(view_type.equals(FLAG)) {
675 current_format = new Format(f, p, on.isSelected());
676 }
677 else {
678 current_format = new Format(f, p, format(Utility.stripNL(editor.getText())));
679 }
680 addFormat(current_format);
681 add.setEnabled(false);
682 remove.setEnabled(true);
683 // Update list selection
684 format_list.setSelectedValue(current_format, true);
685 new_entry = false;
686 ignore = false;
687 }
688 }
689 private class EditorListener
690 extends KeyAdapter {
691 public void keyReleased(KeyEvent event) {
692 String safe = format(editor.getText());
693 if(!ignore && current_format != null) {
694 // We have just performed an edit. Immediately update the format and model.
695 if(safe != null) {
696 current_format.setValue(safe);
697 }
698 else {
699 current_format.setValue("");
700 }
701 gatherer.c_man.configurationChanged();
702 model.refresh();
703 }
704 Entry entry = (Entry) feature.getSelectedItem();
705 String name = entry.toString();
706 if(!(name.length() == 0 && ((String)part.getSelectedItem()).length() == 0) && safe != null && safe.length() != 0 && new_entry) {
707 add.setEnabled(true);
708 }
709 else {
710 add.setEnabled(false);
711 }
712 }
713 }
714 /** This object provides a wrapping around an entry in the format target control, which is tranparent as to whether it is backed by a String or a Classifier. */
715 private class Entry
716 implements Comparable {
717 private Classifier classifier = null;
718 private CustomClassifier custom_classifier = null;
719 private String text = null;
720 public Entry(Object object) {
721 if(object instanceof Classifier) {
722 classifier = (Classifier)object;
723 }
724 if(object instanceof CustomClassifier) {
725 custom_classifier = (CustomClassifier)object;
726 }
727 else if(object instanceof String) {
728 text = (String)object;
729 }
730 else {
731 text = "";
732 }
733 }
734 public Entry(String text) {
735 this.text = text;
736 }
737 public int compareTo(Object object) {
738 if(object == null) {
739 return 1;
740 }
741 if(toString() == null) {
742 return -1;
743 }
744 else {
745 String object_str = object.toString();
746 if(object_str == null) {
747 return 1;
748 }
749 return toString().compareTo(object_str);
750 }
751 }
752 public boolean equals(Object object) {
753 if(compareTo(object) == 0) {
754 return true;
755 }
756 return false;
757 }
758 public Classifier getClassifier() {
759 return classifier;
760 }
761 public CustomClassifier getCustomClassifier() {
762 return custom_classifier;
763 }
764 public Object getFeature() {
765 if(classifier != null) {
766 return classifier;
767 }
768 return text;
769 }
770 public String toString() {
771 if(classifier != null) {
772 String name = classifier.toString();
773 return name.substring(9);
774 }
775 if(custom_classifier != null) {
776 String name = custom_classifier.toString();
777 return name;//.substring(17);
778 }
779 return text;
780 }
781 }
782 private class FeatureListener
783 implements ActionListener {
784 public void actionPerformed(ActionEvent event) {
785 if(!ignore) {
786 current_format = null;
787 Entry entry = (Entry) feature.getSelectedItem();
788 String name = entry.toString();
789 int type = Format.getType(name);
790 switch(type) {
791 case Format.FLAG:
792 // Flags first.
793 part.setEnabled(false);
794 part_pane.remove(part);
795 card_layout.show(view_pane, FLAG);
796 view_type = FLAG;
797 add.setEnabled(true); // One of the options must be selected.
798 break;
799 default:
800 part.setEnabled(true);
801 part_pane.add(part, BorderLayout.CENTER);
802 card_layout.show(view_pane, CUSTOM);
803 view_type = CUSTOM;
804 if(!(name.length() == 0 && ((String)part.getSelectedItem()).length() == 0) && editor.getText().length() != 0) {
805 add.setEnabled(true);
806 }
807 else {
808 add.setEnabled(false);
809 }
810 }
811 control_pane.updateUI();
812 new_entry = true;
813 }
814 }
815 }
816 private class FormatListListener
817 implements ListSelectionListener {
818 public void valueChanged(ListSelectionEvent event) {
819 if(!ignore) {
820 if(!format_list.isSelectionEmpty()) {
821 ignore = true;
822 current_format = (Format)format_list.getSelectedValue();
823 // Try to match the target, remembering the entries within are Entry's
824 Entry an_entry = new Entry(current_format.getFeature());
825 feature.setSelectedItem(an_entry);
826 // Try to match the part.
827 part.setSelectedItem(current_format.getPart());
828 // Now use type to determine what controls are visible, and what have initial values.
829 switch(current_format.getType()) {
830 case Format.FLAG:
831 // Flags first.
832 part.setEnabled(false);
833 part_pane.remove(part);
834 card_layout.show(view_pane, FLAG);
835 view_type = FLAG;
836 // Initial value
837 on.setSelected(current_format.getState());
838 off.setSelected(!current_format.getState());
839 add.setEnabled(false); // Can only update
840 break;
841 default:
842 part.setEnabled(true);
843 part_pane.add(part, BorderLayout.CENTER);
844 card_layout.show(view_pane, CUSTOM);
845 view_type = CUSTOM;
846 // Initial value
847 editor.setText(unformat(current_format.getValue()));
848 add.setEnabled(false);
849 }
850 control_pane.updateUI();
851 ignore = false;
852 preview.setEnabled(true);
853 remove.setEnabled(true);
854 new_entry = false;
855 }
856 else {
857 preview.setEnabled(false);
858 remove.setEnabled(false);
859 }
860 }
861 }
862 }
863 private class InsertListener
864 implements ActionListener {
865 public void actionPerformed(ActionEvent event) {
866 editor.insert((String)special.getSelectedItem(), editor.getCaretPosition());
867 }
868 }
869 private class PartListener
870 implements ActionListener {
871 public void actionPerformed(ActionEvent event) {
872 if(!ignore) {
873 current_format = null;
874 Entry entry = (Entry) feature.getSelectedItem();
875 String name = entry.toString();
876 if(!(name.length() == 0 && ((String)part.getSelectedItem()).length() == 0) && editor.getText().length() != 0) {
877 add.setEnabled(true);
878 }
879 else {
880 add.setEnabled(false);
881 }
882 new_entry = true;
883 }
884 }
885 }
886 private class PreviewListener
887 implements ActionListener {
888 public void actionPerformed(ActionEvent event) {
889 }
890 }
891 private class RemoveListener
892 implements ActionListener {
893 public void actionPerformed(ActionEvent event) {
894 if(!format_list.isSelectionEmpty()) {
895 removeFormat((Format)format_list.getSelectedValue());
896 // Change buttons
897 add.setEnabled(true);
898 remove.setEnabled(false);
899 }
900 }
901 }
902 private class StateListener
903 implements ActionListener {
904 public void actionPerformed(ActionEvent event) {
905 if(!ignore && current_format != null) {
906 // We have just performed an edit. Immediately update the format and model.
907 current_format.setState(on.isSelected());
908 model.refresh();
909 }
910 }
911 }
912 private class ValueListener
913 extends KeyAdapter {
914 public void keyReleased(KeyEvent event) {
915 if(!ignore && current_format != null) {
916 // We have just performed an edit. Immediately update the format and model.
917 current_format.setValue(value.getText());
918 model.refresh();
919 }
920 }
921 }
922 }
923}
Note: See TracBrowser for help on using the repository browser.