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

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

Sunday's work

  • Property svn:keywords set to Author Date Id Revision
File size: 33.7 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.setBackground(Color.white);
446 editor.setCaretPosition(0);
447 editor.setLineWrap(true);
448 editor.setWrapStyleWord(true);
449 editor_label = new JLabel(get("Editor"));
450 editor_label.setHorizontalAlignment(JLabel.CENTER);
451 editor_pane = new JPanel();
452 feature = new JComboBox(feature_model.toArray());
453 feature.setEditable(false);
454 feature_label = new JLabel(get("Feature"));
455 feature_label.setPreferredSize(LABEL_SIZE);
456 format_list = new JList(model);
457 format_list_label = new JLabel(get("Assigned_Formats"));
458 format_list_pane = new JPanel();
459 feature_pane = new JPanel();
460 header_pane = new JPanel();
461 inner_button_pane = new JPanel();
462 inner_state_pane = new JPanel();
463 inner_value_pane = new JPanel();
464 insert = new JButton(get("Insert"));
465 instructions = new JTextArea(get("Instructions"));
466 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
467 instructions.setEditable(false);
468 instructions.setLineWrap(true);
469 instructions.setRows(4);
470 instructions.setWrapStyleWord(true);
471 off = new JToggleButton(get("Off"));
472 off.setSelected(false);
473 on = new JToggleButton(get("On"));
474 on.setSelected(true);
475 options_pane = new JPanel();
476 outer_button_pane = new JPanel();
477 part = new JComboBox(part_model);
478 part.setEditable(false);
479 part_label = new JLabel(get("Part"));
480 part_label.setPreferredSize(LABEL_SIZE);
481 part_pane = new JPanel();
482 preview = new JButton(get("Preview"));
483 preview.setEnabled(false);
484 remove = new JButton(get("Remove"));
485 remove.setEnabled(false);
486 special = new JComboBox(special_model);
487 special_label = new JLabel(get("Special"));
488 special_label.setHorizontalAlignment(JLabel.CENTER);
489 special_pane = new JPanel();
490 state_pane = new JPanel();
491 title = new JLabel(get("Title"));
492 title.setHorizontalAlignment(JLabel.CENTER);
493 title.setOpaque(true);
494 value = new JTextField();
495 value_label = new JLabel(get("Value"));
496 value_pane = new JPanel();
497 view_pane = new JPanel();
498 // Connect
499 add.addActionListener(new AddListener());
500 button_group.add(on);
501 button_group.add(off);
502 editor.addKeyListener(new EditorListener());
503 feature.addActionListener(new FeatureListener());
504 format_list.addListSelectionListener(new FormatListListener());
505 insert.addActionListener(new InsertListener());
506 off.addActionListener(new StateListener());
507 on.addActionListener(new StateListener());
508 part.addActionListener(new PartListener());
509 preview.addActionListener(new PreviewListener());
510 remove.addActionListener(new RemoveListener());
511 value.addKeyListener(new ValueListener());
512 // Layout
513 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5));
514
515 header_pane.setLayout(new BorderLayout());
516 header_pane.add(title, BorderLayout.NORTH);
517 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER);
518
519 format_list_label.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
520
521 format_list_pane.setLayout(new BorderLayout());
522 format_list_pane.add(format_list_label, BorderLayout.NORTH);
523 format_list_pane.add(new JScrollPane(format_list), BorderLayout.CENTER);
524
525 feature_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
526
527 feature_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
528 feature_pane.setLayout(new BorderLayout());
529 feature_pane.add(feature_label, BorderLayout.WEST);
530 feature_pane.add(feature, BorderLayout.CENTER);
531
532 part_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
533
534 part_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
535 part_pane.setLayout(new BorderLayout());
536 part_pane.add(part_label, BorderLayout.WEST);
537 part_pane.add(part, BorderLayout.CENTER);
538
539 options_pane.setLayout(new GridLayout(2,1));
540 options_pane.add(feature_pane);
541 options_pane.add(part_pane);
542
543 inner_state_pane = new JPanel(new GridLayout(1,2));
544 inner_state_pane.add(on);
545 inner_state_pane.add(off);
546
547 state_pane.setLayout(new BorderLayout());
548 state_pane.add(inner_state_pane, BorderLayout.NORTH);
549 state_pane.add(new JPanel(), BorderLayout.CENTER);
550
551 value_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
552
553 inner_value_pane.setLayout(new BorderLayout());
554 inner_value_pane.add(value_label, BorderLayout.WEST);
555 inner_value_pane.add(value, BorderLayout.CENTER);
556
557 value_pane.setLayout(new BorderLayout());
558 value_pane.add(inner_value_pane, BorderLayout.NORTH);
559 value_pane.add(new JPanel(), BorderLayout.CENTER);
560
561 special_pane.setLayout(new GridLayout(3,1));
562 special_pane.add(special_label);
563 special_pane.add(special);
564 special_pane.add(insert);
565
566 editor_pane.setLayout(new BorderLayout());
567 editor_pane.add(editor_label, BorderLayout.NORTH);
568 editor_pane.add(new JScrollPane(editor), BorderLayout.CENTER);
569 editor_pane.add(special_pane, BorderLayout.EAST);
570
571 // Magic for view_pane card layout.
572 view_pane.setLayout(card_layout);
573 view_pane.add(editor_pane, CUSTOM);
574 view_pane.add(state_pane, FLAG);
575 //view_pane.add(value_pane, PARAM);
576
577 inner_button_pane.setLayout(new GridLayout(1,2));
578 inner_button_pane.add(add);
579 inner_button_pane.add(remove);
580
581 outer_button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
582 outer_button_pane.setLayout(new GridLayout(1,1));
583 outer_button_pane.add(inner_button_pane);
584 //outer_button_pane.add(preview);
585
586 control_pane.setLayout(new BorderLayout());
587 control_pane.add(options_pane, BorderLayout.NORTH);
588 control_pane.add(view_pane, BorderLayout.CENTER);
589 control_pane.add(outer_button_pane, BorderLayout.SOUTH);
590
591 setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
592 setLayout(new BorderLayout());
593 add(header_pane, BorderLayout.NORTH);
594 add(format_list_pane, BorderLayout.CENTER);
595 add(control_pane, BorderLayout.SOUTH);
596 ready = true;
597 }
598 public void destroy() {
599 }
600 /** Overriden to ensure that the instructions pane is scrolled to the top.
601 */
602 public void updateUI() {
603 if(ready) {
604 // Rebuild feature model.
605 ArrayList feature_model = new ArrayList();
606 // Add the set options
607 for(int i = 0; i < Format.DEFAULT_FEATURES.length; i++) {
608 feature_model.add(new Entry(Format.DEFAULT_FEATURES[i]));
609 }
610 // Now the classifiers.
611 for(int j = 0; j < manager.classifiers.size(); j++) {
612 feature_model.add(new Entry(manager.classifiers.getClassifier(j)));
613 }
614 feature.setModel(new DefaultComboBoxModel(feature_model.toArray()));
615 if(instructions != null) {
616 instructions.setCaretPosition(0);
617 }
618 }
619 super.updateUI();
620 }
621
622
623
624 /** Formats the formatting string so that it contains safe characters and isn't enclosed in speech marks etc. (Ironic eh?)
625 * @see java.lang.StringBuffer
626 */
627 private String format(String raw) {
628 String safe = null;
629 if(raw != null && raw.length() > 0) {
630 StringBuffer temp = new StringBuffer(raw);
631 // Remove quotes at start and end. Look at my wiggly save three lines of code skills.
632 char start = ' ';
633 while(temp.length() > 0 && (start = temp.charAt(0)) == '\"' || start == '\'') {
634 temp.delete(0, 1);
635 }
636 int length = 0;
637 char end = ' ';
638 while((length = temp.length()) > 0 && (end = temp.charAt(length - 1)) == '\"' || end == '\'') {
639 temp.delete(length - 1, length);
640 }
641 // Now escape quotes within the format string
642 int quote_index = -1;
643 while((quote_index = temp.indexOf("\"", quote_index + 1)) != -1) {
644 temp.replace(quote_index, quote_index + 1, "\\\"");
645 quote_index = quote_index + 1;
646 }
647 // Done.
648 safe = temp.toString();
649 }
650 return safe;
651 }
652 /** Remove safe characters from string replacing them with unsafe ones.
653 * @see java.lang.StringBuffer
654 */
655 private String unformat(String safe) {
656 String raw = null;
657 if(safe != null && safe.length() > 0) {
658 StringBuffer temp = new StringBuffer(safe);
659 int quote_index = -1;
660 while((quote_index = temp.indexOf("\\\"")) != -1) {
661 temp.replace(quote_index, quote_index + 2, "\"");
662 }
663 raw = temp.toString();
664 }
665 return raw;
666 }
667 /** Listens for clicks on the add button, and if the relevant details are provided adds a new format. */
668 private class AddListener
669 implements ActionListener {
670 public void actionPerformed(ActionEvent event) {
671 ignore = true;
672 Entry entry = (Entry)feature.getSelectedItem();
673 Object f = entry.getFeature();
674 String p = (String)part.getSelectedItem();
675 if(view_type.equals(FLAG)) {
676 current_format = new Format(f, p, on.isSelected());
677 }
678 else {
679 current_format = new Format(f, p, format(Utility.stripNL(editor.getText())));
680 }
681 addFormat(current_format);
682 add.setEnabled(false);
683 remove.setEnabled(true);
684 // Update list selection
685 format_list.setSelectedValue(current_format, true);
686 new_entry = false;
687 ignore = false;
688 }
689 }
690 private class EditorListener
691 extends KeyAdapter {
692 public void keyReleased(KeyEvent event) {
693 String safe = format(editor.getText());
694 if(!ignore && current_format != null) {
695 // We have just performed an edit. Immediately update the format and model.
696 if(safe != null) {
697 current_format.setValue(safe);
698 }
699 else {
700 current_format.setValue("");
701 }
702 gatherer.c_man.configurationChanged();
703 model.refresh();
704 }
705 Entry entry = (Entry) feature.getSelectedItem();
706 String name = entry.toString();
707 if(!(name.length() == 0 && ((String)part.getSelectedItem()).length() == 0) && safe != null && safe.length() != 0 && new_entry) {
708 add.setEnabled(true);
709 }
710 else {
711 add.setEnabled(false);
712 }
713 }
714 }
715 /** 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. */
716 private class Entry
717 implements Comparable {
718 private Classifier classifier = null;
719 private CustomClassifier custom_classifier = null;
720 private String text = null;
721 public Entry(Object object) {
722 if(object instanceof Classifier) {
723 classifier = (Classifier)object;
724 }
725 if(object instanceof CustomClassifier) {
726 custom_classifier = (CustomClassifier)object;
727 }
728 else if(object instanceof String) {
729 text = (String)object;
730 }
731 else {
732 text = "";
733 }
734 }
735 public Entry(String text) {
736 this.text = text;
737 }
738 public int compareTo(Object object) {
739 if(object == null) {
740 return 1;
741 }
742 if(toString() == null) {
743 return -1;
744 }
745 else {
746 String object_str = object.toString();
747 if(object_str == null) {
748 return 1;
749 }
750 return toString().compareTo(object_str);
751 }
752 }
753 public boolean equals(Object object) {
754 if(compareTo(object) == 0) {
755 return true;
756 }
757 return false;
758 }
759 public Classifier getClassifier() {
760 return classifier;
761 }
762 public CustomClassifier getCustomClassifier() {
763 return custom_classifier;
764 }
765 public Object getFeature() {
766 if(classifier != null) {
767 return classifier;
768 }
769 return text;
770 }
771 public String toString() {
772 if(classifier != null) {
773 String name = classifier.toString();
774 return name.substring(9);
775 }
776 if(custom_classifier != null) {
777 String name = custom_classifier.toString();
778 return name;//.substring(17);
779 }
780 return text;
781 }
782 }
783 private class FeatureListener
784 implements ActionListener {
785 public void actionPerformed(ActionEvent event) {
786 if(!ignore) {
787 current_format = null;
788 Entry entry = (Entry) feature.getSelectedItem();
789 String name = entry.toString();
790 int type = Format.getType(name);
791 switch(type) {
792 case Format.FLAG:
793 // Flags first.
794 part.setEnabled(false);
795 part_pane.remove(part);
796 card_layout.show(view_pane, FLAG);
797 view_type = FLAG;
798 add.setEnabled(true); // One of the options must be selected.
799 break;
800 default:
801 part.setEnabled(true);
802 part_pane.add(part, BorderLayout.CENTER);
803 card_layout.show(view_pane, CUSTOM);
804 view_type = CUSTOM;
805 if(!(name.length() == 0 && ((String)part.getSelectedItem()).length() == 0) && editor.getText().length() != 0) {
806 add.setEnabled(true);
807 }
808 else {
809 add.setEnabled(false);
810 }
811 }
812 control_pane.updateUI();
813 new_entry = true;
814 }
815 }
816 }
817 private class FormatListListener
818 implements ListSelectionListener {
819 public void valueChanged(ListSelectionEvent event) {
820 if(!ignore) {
821 if(!format_list.isSelectionEmpty()) {
822 ignore = true;
823 current_format = (Format)format_list.getSelectedValue();
824 // Try to match the target, remembering the entries within are Entry's
825 Entry an_entry = new Entry(current_format.getFeature());
826 feature.setSelectedItem(an_entry);
827 // Try to match the part.
828 part.setSelectedItem(current_format.getPart());
829 // Now use type to determine what controls are visible, and what have initial values.
830 switch(current_format.getType()) {
831 case Format.FLAG:
832 // Flags first.
833 part.setEnabled(false);
834 part_pane.remove(part);
835 card_layout.show(view_pane, FLAG);
836 view_type = FLAG;
837 // Initial value
838 on.setSelected(current_format.getState());
839 off.setSelected(!current_format.getState());
840 add.setEnabled(false); // Can only update
841 break;
842 default:
843 part.setEnabled(true);
844 part_pane.add(part, BorderLayout.CENTER);
845 card_layout.show(view_pane, CUSTOM);
846 view_type = CUSTOM;
847 // Initial value
848 editor.setText(unformat(current_format.getValue()));
849 add.setEnabled(false);
850 }
851 control_pane.updateUI();
852 ignore = false;
853 preview.setEnabled(true);
854 remove.setEnabled(true);
855 new_entry = false;
856 }
857 else {
858 preview.setEnabled(false);
859 remove.setEnabled(false);
860 }
861 }
862 }
863 }
864 private class InsertListener
865 implements ActionListener {
866 public void actionPerformed(ActionEvent event) {
867 editor.insert((String)special.getSelectedItem(), editor.getCaretPosition());
868 }
869 }
870 private class PartListener
871 implements ActionListener {
872 public void actionPerformed(ActionEvent event) {
873 if(!ignore) {
874 current_format = null;
875 Entry entry = (Entry) feature.getSelectedItem();
876 String name = entry.toString();
877 if(!(name.length() == 0 && ((String)part.getSelectedItem()).length() == 0) && editor.getText().length() != 0) {
878 add.setEnabled(true);
879 }
880 else {
881 add.setEnabled(false);
882 }
883 new_entry = true;
884 }
885 }
886 }
887 private class PreviewListener
888 implements ActionListener {
889 public void actionPerformed(ActionEvent event) {
890 }
891 }
892 private class RemoveListener
893 implements ActionListener {
894 public void actionPerformed(ActionEvent event) {
895 if(!format_list.isSelectionEmpty()) {
896 removeFormat((Format)format_list.getSelectedValue());
897 // Change buttons
898 add.setEnabled(true);
899 remove.setEnabled(false);
900 }
901 }
902 }
903 private class StateListener
904 implements ActionListener {
905 public void actionPerformed(ActionEvent event) {
906 if(!ignore && current_format != null) {
907 // We have just performed an edit. Immediately update the format and model.
908 current_format.setState(on.isSelected());
909 model.refresh();
910 }
911 }
912 }
913 private class ValueListener
914 extends KeyAdapter {
915 public void keyReleased(KeyEvent event) {
916 if(!ignore && current_format != null) {
917 // We have just performed an edit. Immediately update the format and model.
918 current_format.setValue(value.getText());
919 model.refresh();
920 }
921 }
922 }
923 }
924}
Note: See TracBrowser for help on using the repository browser.