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

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

Initial revision

  • 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 unresolved_commands.add(command);
290 }
291 return true;
292 }
293 return false;
294 }
295 /** Method to remove a format.
296 * @param format The <strong>Format</strong> to remove.
297 */
298 public void removeFormat(Format format) {
299 removeElement(format);
300 gatherer.c_man.configurationChanged();
301 }
302 /** Method which attempts to reparse obvious format commands which previously referenced unresovable Classifiers.
303 */
304 public void reparseUnresolved() {
305 for(int i = 0; i < unresolved_commands.size(); i++) {
306 if(!parse((String)unresolved_commands.get(i), true)) {
307 ///ystem.err.println("*** Error: Command " + unresolved_commands.get(i));
308 }
309 }
310 // Regardless of if they work, clear the commands.
311 unresolved_commands.clear();
312 }
313 /** Method to produce a block of text representing the format commands in this manager, ready to be used in the collection configuration file.
314 * @return A <strong>String</strong> containing a series of format commands.
315 */
316 public String toString() {
317 StringBuffer text = new StringBuffer("");
318 for(int i = 0; i < size(); i++) {
319 Format format = (Format) get(i);
320 text.append(format.toString());
321 text.append("\n");
322 }
323 text.append("\n");
324 return text.toString();
325 }
326 /** Overloaded to call get with both a key and an empty argument array.
327 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
328 * @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.
329 */
330 private String get(String key) {
331 return get(key, null);
332 }
333 /** 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>
334 * 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>.
335 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
336 * @param args A <strong>String[]</strong> used to populate argument fields within the complete String.
337 * @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.
338 * @see org.greenstone.gatherer.Gatherer
339 * @see org.greenstone.gatherer.Dictionary
340 */
341 private String get(String key, String args[]) {
342 if(key.indexOf('.') == -1) {
343 key = "CDM.FormatManager." + key;
344 }
345 return gatherer.dictionary.get(key, args);
346 }
347 private class Control
348 extends JPanel {
349 /** Do we ignore selection changing events (mainly because we're generating them!) */
350 private boolean ignore = false;
351 private boolean new_entry = true;
352 private boolean ready = false;
353 private ButtonGroup button_group = null;
354 private CardLayout card_layout = null;
355 private Dimension LABEL_SIZE = new Dimension(175,25);
356 private Format current_format = null;
357 private String view_type = "custom";
358 private JButton add = null;
359 private JButton insert = null;
360 private JButton preview = null;
361 private JButton remove = null;
362 private JComboBox feature = null;
363 private JComboBox part = null;
364 private JComboBox special = null;
365 private JLabel editor_label = null;
366 private JLabel feature_label = null;
367 private JLabel format_list_label = null;
368 private JLabel part_label = null;
369 private JLabel special_label = null;
370 private JLabel title = null;
371 private JLabel value_label = null;
372 private JList format_list = null;
373 private JPanel blank_pane = null;
374 private JPanel control_pane = null;
375 private JPanel editor_pane = null;
376 private JPanel feature_pane = null;
377 private JPanel format_list_pane = null;
378 private JPanel header_pane = null;
379 private JPanel inner_button_pane = null;
380 private JPanel inner_state_pane = null;
381 private JPanel inner_value_pane = null;
382 private JPanel options_pane = null;
383 private JPanel outer_button_pane = null;
384 private JPanel part_pane = null;
385 private JPanel special_pane = null;
386 private JPanel state_pane = null;
387 private JPanel value_pane = null;
388 private JPanel view_pane = null;
389 private JTextArea editor = null;
390 private JTextArea instructions = null;
391 private JTextField value = null;
392 private JToggleButton off = null;
393 private JToggleButton on = null;
394 private String BLANK = "blank";
395 private String CUSTOM = "custom";
396 private String FLAG = "flag";
397 private String PARAM = "param";
398 private Vector part_model = null;
399 private Vector special_model = null;
400 public Control() {
401 ArrayList feature_model = new ArrayList();
402 // Add the set options
403 for(int i = 0; i < Format.DEFAULT_FEATURES.length; i++) {
404 feature_model.add(new Entry(Format.DEFAULT_FEATURES[i]));
405 }
406 // Now the classifiers.
407 for(int j = 0; j < manager.classifiers.size(); j++) {
408 feature_model.add(new Entry(manager.classifiers.getClassifier(j)));
409 }
410 Collections.sort(feature_model);
411 part_model = new Vector();
412 part_model.add("");//get("Custom"));
413 part_model.add("DateList");
414 part_model.add("HList");
415 part_model.add("Invisible");
416 part_model.add("VList");
417 special_model = new Vector();
418 special_model.add("[Text]");
419 special_model.add("[link]");
420 special_model.add("[/link]");
421 special_model.add("[icon]");
422 special_model.add("[num]");
423 special_model.add("[parent():_]");
424 special_model.add("[parent(Top):_]");
425 special_model.add("[parent(All'_'):_]");
426 Vector elements = gatherer.c_man.msm.getAssignedElements();
427 for(int i = 0; i < elements.size(); i++) {
428 special_model.add("[" + ((ElementWrapper)elements.get(i)).toString() + "]");
429 }
430 Collections.sort(special_model);
431 // Create
432 add = new JButton(get("Add"));
433 add.setEnabled(false);
434 blank_pane = new JPanel();
435 button_group = new ButtonGroup();
436 card_layout = new CardLayout();
437 control_pane = new JPanel();
438 editor = new JTextArea();
439 editor.setCaretPosition(0);
440 editor.setLineWrap(true);
441 editor.setWrapStyleWord(true);
442 editor_label = new JLabel(get("Editor"));
443 editor_label.setHorizontalAlignment(JLabel.CENTER);
444 editor_pane = new JPanel();
445 feature = new JComboBox(feature_model.toArray());
446 feature.setEditable(true);
447 feature_label = new JLabel(get("Feature"));
448 feature_label.setPreferredSize(LABEL_SIZE);
449 format_list = new JList(model);
450 format_list_label = new JLabel(get("Assigned_Formats"));
451 format_list_pane = new JPanel();
452 feature_pane = new JPanel();
453 header_pane = new JPanel();
454 inner_button_pane = new JPanel();
455 inner_state_pane = new JPanel();
456 inner_value_pane = new JPanel();
457 insert = new JButton(get("Insert"));
458 instructions = new JTextArea(get("Instructions"));
459 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
460 instructions.setEditable(false);
461 instructions.setLineWrap(true);
462 instructions.setRows(4);
463 instructions.setWrapStyleWord(true);
464 off = new JToggleButton(get("Off"));
465 off.setSelected(false);
466 on = new JToggleButton(get("On"));
467 on.setSelected(true);
468 options_pane = new JPanel();
469 outer_button_pane = new JPanel();
470 part = new JComboBox(part_model);
471 part.setEditable(true);
472 part_label = new JLabel(get("Part"));
473 part_label.setPreferredSize(LABEL_SIZE);
474 part_pane = new JPanel();
475 preview = new JButton(get("Preview"));
476 preview.setEnabled(false);
477 remove = new JButton(get("Remove"));
478 remove.setEnabled(false);
479 special = new JComboBox(special_model);
480 special_label = new JLabel(get("Special"));
481 special_label.setHorizontalAlignment(JLabel.CENTER);
482 special_pane = new JPanel();
483 state_pane = new JPanel();
484 title = new JLabel(get("Title"));
485 title.setHorizontalAlignment(JLabel.CENTER);
486 title.setOpaque(true);
487 value = new JTextField();
488 value_label = new JLabel(get("Value"));
489 value_pane = new JPanel();
490 view_pane = new JPanel();
491 // Connect
492 add.addActionListener(new AddListener());
493 button_group.add(on);
494 button_group.add(off);
495 editor.addKeyListener(new EditorListener());
496 feature.addActionListener(new FeatureListener());
497 format_list.addListSelectionListener(new FormatListListener());
498 insert.addActionListener(new InsertListener());
499 off.addActionListener(new StateListener());
500 on.addActionListener(new StateListener());
501 part.addActionListener(new PartListener());
502 preview.addActionListener(new PreviewListener());
503 remove.addActionListener(new RemoveListener());
504 value.addKeyListener(new ValueListener());
505 // Layout
506 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5));
507
508 header_pane.setLayout(new BorderLayout());
509 header_pane.add(title, BorderLayout.NORTH);
510 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER);
511
512 format_list_label.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
513
514 format_list_pane.setLayout(new BorderLayout());
515 format_list_pane.add(format_list_label, BorderLayout.NORTH);
516 format_list_pane.add(new JScrollPane(format_list), BorderLayout.CENTER);
517
518 feature_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
519
520 feature_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
521 feature_pane.setLayout(new BorderLayout());
522 feature_pane.add(feature_label, BorderLayout.WEST);
523 feature_pane.add(feature, BorderLayout.CENTER);
524
525 part_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
526
527 part_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
528 part_pane.setLayout(new BorderLayout());
529 part_pane.add(part_label, BorderLayout.WEST);
530 part_pane.add(part, BorderLayout.CENTER);
531
532 options_pane.setLayout(new GridLayout(2,1));
533 options_pane.add(feature_pane);
534 options_pane.add(part_pane);
535
536 inner_state_pane = new JPanel(new GridLayout(1,2));
537 inner_state_pane.add(on);
538 inner_state_pane.add(off);
539
540 state_pane.setLayout(new BorderLayout());
541 state_pane.add(inner_state_pane, BorderLayout.NORTH);
542 state_pane.add(new JPanel(), BorderLayout.CENTER);
543
544 value_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
545
546 inner_value_pane.setLayout(new BorderLayout());
547 inner_value_pane.add(value_label, BorderLayout.WEST);
548 inner_value_pane.add(value, BorderLayout.CENTER);
549
550 value_pane.setLayout(new BorderLayout());
551 value_pane.add(inner_value_pane, BorderLayout.NORTH);
552 value_pane.add(new JPanel(), BorderLayout.CENTER);
553
554 special_pane.setLayout(new GridLayout(3,1));
555 special_pane.add(special_label);
556 special_pane.add(special);
557 special_pane.add(insert);
558
559 editor_pane.setLayout(new BorderLayout());
560 editor_pane.add(editor_label, BorderLayout.NORTH);
561 editor_pane.add(new JScrollPane(editor), BorderLayout.CENTER);
562 editor_pane.add(special_pane, BorderLayout.EAST);
563
564 // Magic for view_pane card layout.
565 view_pane.setLayout(card_layout);
566 view_pane.add(editor_pane, CUSTOM);
567 view_pane.add(state_pane, FLAG);
568 //view_pane.add(value_pane, PARAM);
569
570 inner_button_pane.setLayout(new GridLayout(1,2));
571 inner_button_pane.add(add);
572 inner_button_pane.add(remove);
573
574 outer_button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
575 outer_button_pane.setLayout(new GridLayout(1,1));
576 outer_button_pane.add(inner_button_pane);
577 //outer_button_pane.add(preview);
578
579 control_pane.setLayout(new BorderLayout());
580 control_pane.add(options_pane, BorderLayout.NORTH);
581 control_pane.add(view_pane, BorderLayout.CENTER);
582 control_pane.add(outer_button_pane, BorderLayout.SOUTH);
583
584 setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
585 setLayout(new BorderLayout());
586 add(header_pane, BorderLayout.NORTH);
587 add(format_list_pane, BorderLayout.CENTER);
588 add(control_pane, BorderLayout.SOUTH);
589 ready = true;
590 }
591 public void destroy() {
592 }
593 /** Overriden to ensure that the instructions pane is scrolled to the top.
594 */
595 public void updateUI() {
596 if(ready) {
597 // Rebuild feature model.
598 ArrayList feature_model = new ArrayList();
599 // Add the set options
600 for(int i = 0; i < Format.DEFAULT_FEATURES.length; i++) {
601 feature_model.add(new Entry(Format.DEFAULT_FEATURES[i]));
602 }
603 // Now the classifiers.
604 for(int j = 0; j < manager.classifiers.size(); j++) {
605 feature_model.add(new Entry(manager.classifiers.getClassifier(j)));
606 }
607 feature.setModel(new DefaultComboBoxModel(feature_model.toArray()));
608 if(instructions != null) {
609 instructions.setCaretPosition(0);
610 }
611 }
612 super.updateUI();
613 }
614
615
616
617 /** Formats the formatting string so that it contains safe characters and isn't enclosed in speech marks etc. (Ironic eh?)
618 * @see java.lang.StringBuffer
619 */
620 private String format(String raw) {
621 String safe = null;
622 if(raw != null && raw.length() > 0) {
623 StringBuffer temp = new StringBuffer(raw);
624 // Remove quotes at start and end. Look at my wiggly save three lines of code skills.
625 char start = ' ';
626 while(temp.length() > 0 && (start = temp.charAt(0)) == '\"' || start == '\'') {
627 temp.delete(0, 1);
628 }
629 int length = 0;
630 char end = ' ';
631 while((length = temp.length()) > 0 && (end = temp.charAt(length - 1)) == '\"' || end == '\'') {
632 temp.delete(length - 1, length);
633 }
634 // Now escape quotes within the format string
635 int quote_index = -1;
636 while((quote_index = temp.indexOf("\"", quote_index + 1)) != -1) {
637 temp.replace(quote_index, quote_index + 1, "\\\"");
638 quote_index = quote_index + 1;
639 }
640 // Done.
641 safe = temp.toString();
642 }
643 return safe;
644 }
645 /** Remove safe characters from string replacing them with unsafe ones.
646 * @see java.lang.StringBuffer
647 */
648 private String unformat(String safe) {
649 String raw = null;
650 if(safe != null && safe.length() > 0) {
651 StringBuffer temp = new StringBuffer(safe);
652 int quote_index = -1;
653 while((quote_index = temp.indexOf("\\\"")) != -1) {
654 temp.replace(quote_index, quote_index + 2, "\"");
655 }
656 raw = temp.toString();
657 }
658 return raw;
659 }
660 /** Listens for clicks on the add button, and if the relevant details are provided adds a new format. */
661 private class AddListener
662 implements ActionListener {
663 public void actionPerformed(ActionEvent event) {
664 ignore = true;
665 Entry entry = (Entry)feature.getSelectedItem();
666 Object f = entry.getFeature();
667 String p = (String)part.getSelectedItem();
668 if(view_type.equals(FLAG)) {
669 current_format = new Format(f, p, on.isSelected());
670 }
671 else {
672 current_format = new Format(f, p, format(Utility.stripNL(editor.getText())));
673 }
674 addFormat(current_format);
675 add.setEnabled(false);
676 remove.setEnabled(true);
677 // Update list selection
678 format_list.setSelectedValue(current_format, true);
679 new_entry = false;
680 ignore = false;
681 }
682 }
683 private class EditorListener
684 extends KeyAdapter {
685 public void keyReleased(KeyEvent event) {
686 String safe = format(editor.getText());
687 if(!ignore && current_format != null) {
688 // We have just performed an edit. Immediately update the format and model.
689 if(safe != null) {
690 current_format.setValue(safe);
691 }
692 else {
693 current_format.setValue("");
694 }
695 gatherer.c_man.configurationChanged();
696 model.refresh();
697 }
698 Entry entry = (Entry) feature.getSelectedItem();
699 String name = entry.toString();
700 if(!(name.length() == 0 && ((String)part.getSelectedItem()).length() == 0) && safe != null && safe.length() != 0 && new_entry) {
701 add.setEnabled(true);
702 }
703 else {
704 add.setEnabled(false);
705 }
706 }
707 }
708 /** 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. */
709 private class Entry
710 implements Comparable {
711 private Classifier classifier = null;
712 private CustomClassifier custom_classifier = null;
713 private String text = null;
714 public Entry(Object object) {
715 if(object instanceof Classifier) {
716 classifier = (Classifier)object;
717 }
718 if(object instanceof CustomClassifier) {
719 custom_classifier = (CustomClassifier)object;
720 }
721 else if(object instanceof String) {
722 text = (String)object;
723 }
724 else {
725 text = "";
726 }
727 }
728 public Entry(String text) {
729 this.text = text;
730 }
731 public int compareTo(Object object) {
732 if(object == null) {
733 return 1;
734 }
735 if(toString() == null) {
736 return -1;
737 }
738 else {
739 String object_str = object.toString();
740 if(object_str == null) {
741 return 1;
742 }
743 return toString().compareTo(object_str);
744 }
745 }
746 public boolean equals(Object object) {
747 if(compareTo(object) == 0) {
748 return true;
749 }
750 return false;
751 }
752 public Classifier getClassifier() {
753 return classifier;
754 }
755 public CustomClassifier getCustomClassifier() {
756 return custom_classifier;
757 }
758 public Object getFeature() {
759 if(classifier != null) {
760 return classifier;
761 }
762 return text;
763 }
764 public String toString() {
765 if(classifier != null) {
766 String name = classifier.toString();
767 return name.substring(9);
768 }
769 if(custom_classifier != null) {
770 String name = custom_classifier.toString();
771 return name;//.substring(17);
772 }
773 return text;
774 }
775 }
776 private class FeatureListener
777 implements ActionListener {
778 public void actionPerformed(ActionEvent event) {
779 if(!ignore) {
780 current_format = null;
781 Entry entry = (Entry) feature.getSelectedItem();
782 String name = entry.toString();
783 int type = Format.getType(name);
784 switch(type) {
785 case Format.FLAG:
786 // Flags first.
787 part.setEnabled(false);
788 part_pane.remove(part);
789 card_layout.show(view_pane, FLAG);
790 view_type = FLAG;
791 add.setEnabled(true); // One of the options must be selected.
792 break;
793 default:
794 part.setEnabled(true);
795 part_pane.add(part, BorderLayout.CENTER);
796 card_layout.show(view_pane, CUSTOM);
797 view_type = CUSTOM;
798 if(!(name.length() == 0 && ((String)part.getSelectedItem()).length() == 0) && editor.getText().length() != 0) {
799 add.setEnabled(true);
800 }
801 else {
802 add.setEnabled(false);
803 }
804 }
805 control_pane.updateUI();
806 new_entry = true;
807 }
808 }
809 }
810 private class FormatListListener
811 implements ListSelectionListener {
812 public void valueChanged(ListSelectionEvent event) {
813 if(!ignore) {
814 if(!format_list.isSelectionEmpty()) {
815 ignore = true;
816 current_format = (Format)format_list.getSelectedValue();
817 // Try to match the target, remembering the entries within are Entry's
818 Entry an_entry = new Entry(current_format.getFeature());
819 feature.setSelectedItem(an_entry);
820 // Try to match the part.
821 part.setSelectedItem(current_format.getPart());
822 // Now use type to determine what controls are visible, and what have initial values.
823 switch(current_format.getType()) {
824 case Format.FLAG:
825 // Flags first.
826 part.setEnabled(false);
827 part_pane.remove(part);
828 card_layout.show(view_pane, FLAG);
829 view_type = FLAG;
830 // Initial value
831 on.setSelected(current_format.getState());
832 off.setSelected(!current_format.getState());
833 add.setEnabled(false); // Can only update
834 break;
835 default:
836 part.setEnabled(true);
837 part_pane.add(part, BorderLayout.CENTER);
838 card_layout.show(view_pane, CUSTOM);
839 view_type = CUSTOM;
840 // Initial value
841 editor.setText(unformat(current_format.getValue()));
842 add.setEnabled(false);
843 }
844 control_pane.updateUI();
845 ignore = false;
846 preview.setEnabled(true);
847 remove.setEnabled(true);
848 new_entry = false;
849 }
850 else {
851 preview.setEnabled(false);
852 remove.setEnabled(false);
853 }
854 }
855 }
856 }
857 private class InsertListener
858 implements ActionListener {
859 public void actionPerformed(ActionEvent event) {
860 editor.insert((String)special.getSelectedItem(), editor.getCaretPosition());
861 }
862 }
863 private class PartListener
864 implements ActionListener {
865 public void actionPerformed(ActionEvent event) {
866 if(!ignore) {
867 current_format = null;
868 Entry entry = (Entry) feature.getSelectedItem();
869 String name = entry.toString();
870 if(!(name.length() == 0 && ((String)part.getSelectedItem()).length() == 0) && editor.getText().length() != 0) {
871 add.setEnabled(true);
872 }
873 else {
874 add.setEnabled(false);
875 }
876 new_entry = true;
877 }
878 }
879 }
880 private class PreviewListener
881 implements ActionListener {
882 public void actionPerformed(ActionEvent event) {
883 }
884 }
885 private class RemoveListener
886 implements ActionListener {
887 public void actionPerformed(ActionEvent event) {
888 if(!format_list.isSelectionEmpty()) {
889 removeFormat((Format)format_list.getSelectedValue());
890 // Change buttons
891 add.setEnabled(true);
892 remove.setEnabled(false);
893 }
894 }
895 }
896 private class StateListener
897 implements ActionListener {
898 public void actionPerformed(ActionEvent event) {
899 if(!ignore && current_format != null) {
900 // We have just performed an edit. Immediately update the format and model.
901 current_format.setState(on.isSelected());
902 model.refresh();
903 }
904 }
905 }
906 private class ValueListener
907 extends KeyAdapter {
908 public void keyReleased(KeyEvent event) {
909 if(!ignore && current_format != null) {
910 // We have just performed an edit. Immediately update the format and model.
911 current_format.setValue(value.getText());
912 model.refresh();
913 }
914 }
915 }
916 }
917}
Note: See TracBrowser for help on using the repository browser.