source: trunk/gli/src/org/greenstone/gatherer/Dictionary.java@ 4928

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

Major CDM rewrite so it uses DOM.

  • Property svn:keywords set to Author Date Id Revision
File size: 21.4 KB
Line 
1package org.greenstone.gatherer;
2/**
3 *#########################################################################
4 *
5 * A component of the Gatherer application, part of the Greenstone digital
6 * library suite from the New Zealand Digital Library Project at the
7 * University of Waikato, New Zealand.
8 *
9 * <BR><BR>
10 *
11 * Author: John Thompson, Greenstone Digital Library, University of Waikato
12 *
13 * <BR><BR>
14 *
15 * Copyright (C) 1999 New Zealand Digital Library Project
16 *
17 * <BR><BR>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * <BR><BR>
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * <BR><BR>
32 *
33 * You should have received a copy of the GNU General Public License
34 * along with this program; if not, write to the Free Software
35 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
36 *########################################################################
37 */
38import java.util.*;
39import javax.swing.*;
40import javax.swing.plaf.*;
41import javax.swing.text.*;
42import javax.swing.tree.*;
43import org.greenstone.gatherer.gui.border.TitledBorder;
44import org.greenstone.gatherer.util.DictionaryTreeNode;
45import org.greenstone.gatherer.util.MutableComboBoxEntry;
46import org.greenstone.gatherer.util.ArrayTools;
47import org.greenstone.gatherer.util.Utility;
48/** Extends the ResourceBundle class to allow for the automatic insertion of arguments. Note that the key names beginning Farg are reserved for formatting. <BR>
49 * <BR>
50 * Property files usable by this class have the Backus-Naur form: <BR>
51 * <BR>
52 * FileLine ::= Comment | Mapping <BR>
53 * Comment ::= '#' SString <BR>
54 * Mapping ::= NZString ':' SString ( Argument SString )* <BR>
55 * NZString ::= ( Char | Int ) SString <BR>
56 * Argument ::= '{' Int '}' <BR>
57 * SString ::= String . ['"','#',...] -> ['\"','\#',...] <BR>
58 * <BR>
59 * In order to add a new dictionary Locale, simply copy the existing dictionary.properties files, replace the values (Strings after the ':') with the new language specific ones being careful to maintain formatting and Gatherer placed arguments, then save the new dictionary as: <br>
60 * <BR>
61 * dictionary_<I>locale</I>.properties<BR>
62 * <BR>
63 * where locale is made of two two-letter codes seperated by an underscore. The first code is in lower-case and defines the language. The second is in upper-case and defines the country. For example the default dictionary could also correctly be called:
64 * <BR>
65 * dictionary_en_NZ.properties<BR>
66 * @author John Thompson, Greenstone Digital Library, University of Waikato
67 * @version 2.3
68 */
69public class Dictionary
70 extends HashMap {
71 /** A String which more explicitly states the Locale of this dictionary. */
72 public String language = null;
73 /** A static reference to ourself. */
74 static public Dictionary self;
75 /** The font used when displaying various html text. */
76 private FontUIResource font = null;
77 /** A reference to remind us of the current locale. */
78 private Locale locale = null;
79 /** The ResourceBundle which contains the raw key-value mappings. Loaded from a file named "dictionary<I>locale</I>.properties*/
80 private ResourceBundle dictionary = null;
81 /** Constructs the Dictionary class by first checking if a Locale has been set. If not the default locale is used, and a ResourceBundle is created. Finally a single important String, Language, is made available outside the class so a more read-able version of the Locale of this Dictionary is present.
82 * @param locale The <strong>Locale</strong> used to load the desired dictionary resource bundle.
83 */
84 public Dictionary(Locale locale, FontUIResource font) {
85 super();
86 this.self = this;
87 // Initialize.
88 this.font = font;
89 if(locale == null) {
90 this.locale = Locale.getDefault();
91 }
92 else {
93 this.locale = locale;
94 Locale.setDefault(locale);
95 }
96 dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, this.locale);
97 // Now quickly read in language name.
98 language = dictionary.getString("Language");
99 }
100 /** Change the currently loaded dictionary and update registered (ie dynamic) components as possible. */
101 public void changeDictionary(Locale locale) {
102 this.locale = locale;
103 // Load new dictionary
104 dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, locale);
105 // Refresh all registered component
106 for(Iterator keys = keySet().iterator(); keys.hasNext(); ) {
107 Object component = keys.next();
108 String[] args = (String[]) get(component);
109 if(component instanceof AbstractButton) {
110 register((AbstractButton)component, args, true);
111 }
112 else if(component instanceof JComboBox) {
113 register((JComboBox)component, args, true);
114 }
115 else if(component instanceof JDialog) {
116 register((JDialog)component, args, true);
117 }
118 else if(component instanceof JFrame) {
119 register((JFrame)component, args, true);
120 }
121 else if(component instanceof JLabel) {
122 register((JLabel)component, args, true);
123 }
124 else if(component instanceof JTabbedPane) {
125 register((JTabbedPane)component, args, true);
126 }
127 else if(component instanceof JTextComponent) {
128 register((JTextComponent)component, args, true);
129 }
130 else if(component instanceof JTree) {
131 register((JTree)component, args, true);
132 }
133 else if(component instanceof TitledBorder) {
134 register((TitledBorder)component, args, true);
135 }
136 args = null;
137 component = null;
138 }
139 }
140 /** Remove the component from our registered components list. */
141 public void deregister(Object component) {
142 remove(component);
143 }
144
145 /** Overloaded to call get with both a key and an empty argument array.
146 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
147 * @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.
148 */
149 public String get(String key) {
150 return get(key, (String[])null);
151 }
152 /** Convienence method with transforms the second string argument into a string array. */
153 public String get(String key, String arg) {
154 String[] args = new String[1];
155 args[0] = arg;
156 return get(key, args);
157 }
158 /** 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>
159 * 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>.
160 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
161 * @param args A <strong>String[]</strong> used to populate argument fields within the complete String.
162 * @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.
163 */
164 public String get(String key, String args[]) {
165 try {
166 String initial = dictionary.getString(key);
167 // If the string contains arguments we have to insert them.
168 String complete = "";
169 // While we still have initial string left.
170 while(initial.length() > 0 && initial.indexOf('{') != -1 && initial.indexOf('}') != -1) {
171 // Remove preamble
172 int opening = initial.indexOf('{');
173 int closing = initial.indexOf('}');
174 complete = complete + initial.substring(0, opening);
175 // Parse arg_num
176 String arg_str = initial.substring(opening + 1, closing);
177 int arg_num = Integer.parseInt(arg_str);
178 if(closing + 1 < initial.length()) {
179 initial = initial.substring(closing + 1);
180 }
181 else {
182 initial = "";
183 }
184 // Insert argument
185 if(args != null && 0 <= arg_num && arg_num < args.length) {
186 complete = complete + args[arg_num];
187 }
188 else if(arg_num >= 32) {
189 String f_subargs[] = new String[1];
190 if(font != null) {
191 f_subargs[0] = font.getFontName();
192 }
193 else {
194 f_subargs[0] = "Arial";
195 }
196 complete = complete + get("Farg" + arg_num, f_subargs);
197 }
198 }
199 return complete + initial;
200 }
201 catch (Exception e) {
202 if(!key.endsWith("_Tooltip")) {
203 Gatherer.println("Missing value for key: " + key);
204 }
205 //e.printStackTrace();
206 return key;
207 }
208 }
209 /** Retrieve the two letter code of the current language we are using, according to the stored locale.
210 * @return A <strong>String</strong> containing the two letter ISO639 language code.
211 */
212 public String getLanguage() {
213 return locale.getLanguage();
214 }
215 /** Register an abstract button component. */
216 public void register(AbstractButton component, String[] args, boolean already_registered) {
217 if(component != null) {
218 // Determine the key
219 String key = "";
220 if(!already_registered) {
221 key = component.getText();
222 }
223 else {
224 key = args[args.length - 1];
225 }
226 // Update the component using the AWTEvent queue
227 String value = get(key, args);
228 String tooltip = get(key + "_Tooltip", (String[])null);
229 ChangeTask task = new AbstractButtonChangeTask(component, key, value, tooltip);
230 SwingUtilities.invokeLater(task);
231 // Register as necessary
232 if(!already_registered) {
233 args = ArrayTools.add(args, key);
234 put(component, args);
235 }
236 }
237 }
238 /** Register a combobox component. */
239 public void register(JComboBox component, String[] args, boolean already_registered) {
240 if(component != null) {
241 // If not already registered then args will be null.
242 if(!already_registered) {
243 args = new String[component.getItemCount()];
244 }
245 // Retrieve the tooltip. The key is mostly derived from the comboboxes name.
246 String key = component.getName();
247 String tooltip = get(key + "_Tooltip", (String[])null);
248 ChangeTask task = new JComboBoxChangeTask(component, key, -1, tooltip);
249 SwingUtilities.invokeLater(task);
250 // Iterate through the combobox, updating values and recording the original key of each item in args.
251 for(int i = 0; i < args.length; i++) {
252 if(args[i] == null) {
253 args[i] = component.getItemAt(i).toString();
254 }
255 String value = get(args[i], (String[])null);
256 task = new JComboBoxChangeTask(component, key, i, value);
257 SwingUtilities.invokeLater(task);
258 }
259 // Register if necessary
260 if(!already_registered) {
261 put(component, args);
262 }
263 }
264 }
265 /** Register a dialog component. */
266 public void register(JDialog component, String[] args, boolean already_registered) {
267 if(component != null) {
268 // Determine the key
269 String key = "";
270 if(!already_registered) {
271 key = component.getTitle();
272 }
273 else {
274 key = args[args.length - 1];
275 }
276 // Update the component using the AWTEvent queue
277 String value = get(key, args);
278 ChangeTask task = new JDialogChangeTask(component, key, value);
279 SwingUtilities.invokeLater(task);
280 // Register as necessary
281 if(!already_registered) {
282 args = ArrayTools.add(args, key);
283 put(component, args);
284 }
285 }
286 }
287 /** Register a frame component. */
288 public void register(JFrame component, String[] args, boolean already_registered) {
289 if(component != null) {
290 // Determine the key
291 String key = "";
292 if(!already_registered) {
293 key = component.getTitle();
294 }
295 else {
296 key = args[args.length - 1];
297 }
298 // Update the component using the AWTEvent queue
299 String value = get(key, args);
300 ChangeTask task = new JFrameChangeTask(component, key, value);
301 SwingUtilities.invokeLater(task);
302 // Register as necessary
303 if(!already_registered) {
304 args = ArrayTools.add(args, key);
305 put(component, args);
306 }
307 }
308 }
309 /** Register a label component. */
310 public void register(JLabel component, String[] args, boolean already_registered) {
311 if(component != null) {
312 // Determine the key
313 String key = "";
314 if(!already_registered) {
315 key = component.getText();
316 }
317 else {
318 key = args[args.length - 1];
319 }
320 // Update the component using the AWTEvent queue
321 String value = get(key, args);
322 ChangeTask task = new JLabelChangeTask(component, key, value);
323 SwingUtilities.invokeLater(task);
324 // Register as necessary
325 if(!already_registered) {
326 args = ArrayTools.add(args, key);
327 put(component, args);
328 }
329 }
330 }
331 /** Register a tab pane component. */
332 public void register(JTabbedPane component, String[] args, boolean already_registered) {
333 if(component != null) {
334 // If not already registered then args will be null.
335 if(!already_registered) {
336 args = new String[component.getTabCount()];
337 }
338 // Iterate through the tabbed panes tabs, updating values and recording the original key of each item in args.
339 for(int i = 0; i < args.length; i++) {
340 if(args[i] == null) {
341 args[i] = component.getTitleAt(i);
342 }
343 String value = get(args[i], (String[])null);
344 String tooltip = get(args[i] + "_Tooltip", (String[])null);
345 ChangeTask task = new JTabbedPaneChangeTask(component, args[i], i, value, tooltip);
346 SwingUtilities.invokeLater(task);
347 }
348 // Register if necessary
349 if(!already_registered) {
350 put(component, args);
351 }
352 }
353 }
354 /** Register a text component. */
355 public void register(JTextComponent component, String[] args, boolean already_registered) {
356 if(component != null) {
357 // Determine the key
358 String key = "";
359 if(!already_registered) {
360 key = component.getText();
361 }
362 else {
363 key = args[args.length - 1];
364 }
365 // Update the component using the AWTEvent queue
366 String value = get(key, args);
367 String tooltip = get(key + "_Tooltip", (String[])null);
368 ChangeTask task = new JTextComponentChangeTask(component, key, value, tooltip);
369 SwingUtilities.invokeLater(task);
370 // Register as necessary
371 if(!already_registered) {
372 args = ArrayTools.add(args, key);
373 put(component, args);
374 }
375 }
376 }
377 /** Register a tree component. */
378 public void register(JTree component, String[] args, boolean already_registered) {
379 if(component != null) {
380 // Retrieve the tooltip using the components name
381 String key = component.getName();
382 String tooltip = get(key + "_Tooltip", (String[])null);
383 ChangeTask task = new JTreeChangeTask(component, key, tooltip);
384 SwingUtilities.invokeLater(task);
385 // A tree can never be previously registered. In otherwords the keys are harvested each time. Thus for a tree to remain consistant its up to the implementer to implement DictionaryTreeNode for the tree nodes!
386 ArrayList nodes = new ArrayList();
387 nodes.add(component.getModel().getRoot());
388 while(nodes.size() > 0) {
389 DictionaryTreeNode node = (DictionaryTreeNode) nodes.remove(0);
390 // Update
391 String value = get(node.getKey(), (String[])null);
392 task = new JTreeChangeTask(component, node.getKey(), node, value);
393 SwingUtilities.invokeLater(task);
394 // Add children to nodes
395 for(int i = 0; i < node.getChildCount(); i++) {
396 nodes.add(node.getChildAt(i));
397 }
398 }
399 }
400 }
401 /** Register a titled border component. */
402 public void register(TitledBorder component, String[] args, boolean already_registered) {
403 if(component != null) {
404 // Determine the key
405 String key = "";
406 if(!already_registered) {
407 key = component.getTitle();
408 }
409 else {
410 key = args[args.length - 1];
411 }
412 // Update the component using the AWTEvent queue
413 String value = get(key, args);
414 ChangeTask task = new TitledBorderChangeTask(component, key, value);
415 SwingUtilities.invokeLater(task);
416 // Register as necessary
417 if(!already_registered) {
418 args = ArrayTools.add(args, key);
419 put(component, args);
420 }
421 }
422 }
423 /** A get method called internally by components that have been previously been registered, which means that arg[index] is the original key value. Index is usually the last entry in the array, however this is not true for comboboxes, tabbed panes and trees. */
424 private String get(String[] args, int index) {
425 return get(args[index], args);
426 }
427
428 private abstract class ChangeTask
429 implements Runnable {
430 protected String key;
431 protected String value;
432 public ChangeTask(String key, String value) {
433 this.key = key;
434 this.value = value;
435 }
436 public void run() {
437 }
438 }
439 /** Update the text and tooltip for this button. */
440 private class AbstractButtonChangeTask
441 extends ChangeTask {
442 private AbstractButton component;
443 private String tooltip;
444 public AbstractButtonChangeTask(AbstractButton component, String key, String value, String tooltip) {
445 super(key, value);
446 this.component = component;
447 this.tooltip = tooltip;
448 }
449 public void run() {
450 component.setText(value);
451 if(!tooltip.equals(key+"_Tooltip")) {
452 component.setToolTipText(tooltip);
453 }
454 else {
455 component.setToolTipText(null);
456 }
457 }
458 }
459 /** Update the text associated with a combobox. If the index used is -1 then we are setting the tooltip for this combobox. */
460 private class JComboBoxChangeTask
461 extends ChangeTask {
462 private int index;
463 private JComboBox component;
464 public JComboBoxChangeTask(JComboBox component, String key, int index, String value) {
465 super(key, value);
466 this.component = component;
467 this.index = index;
468 }
469 public void run() {
470 if(index != -1) {
471 try {
472 MutableComboBoxEntry entry = (MutableComboBoxEntry)component.getItemAt(index);
473 entry.setText(value);
474 }
475 catch (Exception error) {
476 }
477 }
478 else {
479 if(!value.equals(key+"_Tooltip")) {
480 component.setToolTipText(value);
481 }
482 else {
483 component.setToolTipText(null);
484 }
485 }
486 }
487 }
488 /** Update the title of this dialog. */
489 private class JDialogChangeTask
490 extends ChangeTask {
491 private JDialog component;
492 public JDialogChangeTask(JDialog component, String key, String value) {
493 super(key, value);
494 this.component = component;
495 }
496 public void run() {
497 component.setTitle(value);
498 }
499 }
500 /** Update the title of this frame. */
501 private class JFrameChangeTask
502 extends ChangeTask {
503 private JFrame component;
504 public JFrameChangeTask(JFrame component, String key, String value) {
505 super(key, value);
506 this.component = component;
507 }
508 public void run() {
509 component.setTitle(value);
510 }
511 }
512 /** Update the text of this label. */
513 private class JLabelChangeTask
514 extends ChangeTask {
515 private JLabel component;
516 public JLabelChangeTask(JLabel component, String key, String value) {
517 super(key, value);
518 this.component = component;
519 }
520 public void run() {
521 component.setText(value);
522 }
523 }
524 /** Updates a tabbed panes tab title and tooltip. */
525 private class JTabbedPaneChangeTask
526 extends ChangeTask {
527 private int index;
528 private JTabbedPane component;
529 private String tooltip;
530 public JTabbedPaneChangeTask(JTabbedPane component, String key, int index, String value, String tooltip) {
531 super(key, value);
532 this.component = component;
533 this.index = index;
534 this.tooltip = tooltip;
535 }
536 public void run() {
537 component.setTitleAt(index, value);
538 if(!tooltip.equals(key+"_Tooltip")) {
539 component.setToolTipTextAt(index, tooltip);
540 }
541 else {
542 component.setToolTipTextAt(index, null);
543 }
544 }
545 }
546 /** Update the text and tooltip of this text component. */
547 private class JTextComponentChangeTask
548 extends ChangeTask {
549 private JTextComponent component;
550 private String tooltip;
551 public JTextComponentChangeTask(JTextComponent component, String key, String value, String tooltip) {
552 super(key, value);
553 this.component = component;
554 this.tooltip = tooltip;
555 }
556 public void run() {
557 component.setText(value);
558 if(!tooltip.equals(key+"_Tooltip")) {
559 component.setToolTipText(tooltip);
560 }
561 else {
562 component.setToolTipText(null);
563 }
564 }
565 }
566 /** Update the tooltip of a tree and its tree node's labels. Shouldn't really ever be used on a dynamic tree, but is quite useful for a 'contents' tree type control. */
567 private class JTreeChangeTask
568 extends ChangeTask {
569 private DictionaryTreeNode node;
570 private JTree component;
571 public JTreeChangeTask(JTree component, String key, String value) {
572 super(key, value);
573 this.component = component;
574 }
575 public JTreeChangeTask(JTree component, String key, DictionaryTreeNode node, String value) {
576 super(key, value);
577 this.component = component;
578 this.node = node;
579 }
580 public void run() {
581 if(value != null) {
582 node.setText(value);
583 ((DefaultTreeModel)component.getModel()).nodeChanged((TreeNode)node);
584 }
585 // Set the tool tip
586 else {
587 if(!value.equals(key+"_Tooltip")) {
588 component.setToolTipText(value);
589 }
590 else {
591 component.setToolTipText(null);
592 }
593 }
594 }
595 }
596 /** Update the title of this titled border. */
597 private class TitledBorderChangeTask
598 extends ChangeTask {
599 private TitledBorder component;
600 public TitledBorderChangeTask(TitledBorder component, String key, String value) {
601 super(key, value);
602 this.component = component;
603 }
604 public void run() {
605 component.setTitle(value);
606 component.getParent().repaint();
607 }
608 }
609}
610
611
612
613
Note: See TracBrowser for help on using the repository browser.