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

Last change on this file since 4436 was 4363, checked in by kjdon, 21 years ago

re-tabbed the code for java

  • 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 return key;
206 }
207 }
208 /** Retrieve the two letter code of the current language we are using, according to the stored locale.
209 * @return A <strong>String</strong> containing the two letter ISO639 language code.
210 */
211 public String getLanguage() {
212 return locale.getLanguage();
213 }
214 /** Register an abstract button component. */
215 public void register(AbstractButton component, String[] args, boolean already_registered) {
216 if(component != null) {
217 // Determine the key
218 String key = "";
219 if(!already_registered) {
220 key = component.getText();
221 }
222 else {
223 key = args[args.length - 1];
224 }
225 // Update the component using the AWTEvent queue
226 String value = get(key, args);
227 String tooltip = get(key + "_Tooltip", (String[])null);
228 ChangeTask task = new AbstractButtonChangeTask(component, key, value, tooltip);
229 SwingUtilities.invokeLater(task);
230 // Register as necessary
231 if(!already_registered) {
232 args = ArrayTools.add(args, key);
233 put(component, args);
234 }
235 }
236 }
237 /** Register a combobox component. */
238 public void register(JComboBox component, String[] args, boolean already_registered) {
239 if(component != null) {
240 // If not already registered then args will be null.
241 if(!already_registered) {
242 args = new String[component.getItemCount()];
243 }
244 // Retrieve the tooltip. The key is mostly derived from the comboboxes name.
245 String key = component.getName();
246 String tooltip = get(key + "_Tooltip", (String[])null);
247 ChangeTask task = new JComboBoxChangeTask(component, key, -1, tooltip);
248 SwingUtilities.invokeLater(task);
249 // Iterate through the combobox, updating values and recording the original key of each item in args.
250 for(int i = 0; i < args.length; i++) {
251 if(args[i] == null) {
252 args[i] = component.getItemAt(i).toString();
253 }
254 String value = get(args[i], (String[])null);
255 task = new JComboBoxChangeTask(component, key, i, value);
256 SwingUtilities.invokeLater(task);
257 }
258 // Register if necessary
259 if(!already_registered) {
260 put(component, args);
261 }
262 }
263 }
264 /** Register a dialog component. */
265 public void register(JDialog component, String[] args, boolean already_registered) {
266 if(component != null) {
267 // Determine the key
268 String key = "";
269 if(!already_registered) {
270 key = component.getTitle();
271 }
272 else {
273 key = args[args.length - 1];
274 }
275 // Update the component using the AWTEvent queue
276 String value = get(key, args);
277 ChangeTask task = new JDialogChangeTask(component, key, value);
278 SwingUtilities.invokeLater(task);
279 // Register as necessary
280 if(!already_registered) {
281 args = ArrayTools.add(args, key);
282 put(component, args);
283 }
284 }
285 }
286 /** Register a frame component. */
287 public void register(JFrame component, String[] args, boolean already_registered) {
288 if(component != null) {
289 // Determine the key
290 String key = "";
291 if(!already_registered) {
292 key = component.getTitle();
293 }
294 else {
295 key = args[args.length - 1];
296 }
297 // Update the component using the AWTEvent queue
298 String value = get(key, args);
299 ChangeTask task = new JFrameChangeTask(component, key, value);
300 SwingUtilities.invokeLater(task);
301 // Register as necessary
302 if(!already_registered) {
303 args = ArrayTools.add(args, key);
304 put(component, args);
305 }
306 }
307 }
308 /** Register a label component. */
309 public void register(JLabel component, String[] args, boolean already_registered) {
310 if(component != null) {
311 // Determine the key
312 String key = "";
313 if(!already_registered) {
314 key = component.getText();
315 }
316 else {
317 key = args[args.length - 1];
318 }
319 // Update the component using the AWTEvent queue
320 String value = get(key, args);
321 ChangeTask task = new JLabelChangeTask(component, key, value);
322 SwingUtilities.invokeLater(task);
323 // Register as necessary
324 if(!already_registered) {
325 args = ArrayTools.add(args, key);
326 put(component, args);
327 }
328 }
329 }
330 /** Register a tab pane component. */
331 public void register(JTabbedPane component, String[] args, boolean already_registered) {
332 if(component != null) {
333 // If not already registered then args will be null.
334 if(!already_registered) {
335 args = new String[component.getTabCount()];
336 }
337 // Iterate through the tabbed panes tabs, updating values and recording the original key of each item in args.
338 for(int i = 0; i < args.length; i++) {
339 if(args[i] == null) {
340 args[i] = component.getTitleAt(i);
341 }
342 String value = get(args[i], (String[])null);
343 String tooltip = get(args[i] + "_Tooltip", (String[])null);
344 ChangeTask task = new JTabbedPaneChangeTask(component, args[i], i, value, tooltip);
345 SwingUtilities.invokeLater(task);
346 }
347 // Register if necessary
348 if(!already_registered) {
349 put(component, args);
350 }
351 }
352 }
353 /** Register a text component. */
354 public void register(JTextComponent component, String[] args, boolean already_registered) {
355 if(component != null) {
356 // Determine the key
357 String key = "";
358 if(!already_registered) {
359 key = component.getText();
360 }
361 else {
362 key = args[args.length - 1];
363 }
364 // Update the component using the AWTEvent queue
365 String value = get(key, args);
366 String tooltip = get(key + "_Tooltip", (String[])null);
367 ChangeTask task = new JTextComponentChangeTask(component, key, value, tooltip);
368 SwingUtilities.invokeLater(task);
369 // Register as necessary
370 if(!already_registered) {
371 args = ArrayTools.add(args, key);
372 put(component, args);
373 }
374 }
375 }
376 /** Register a tree component. */
377 public void register(JTree component, String[] args, boolean already_registered) {
378 if(component != null) {
379 // Retrieve the tooltip using the components name
380 String key = component.getName();
381 String tooltip = get(key + "_Tooltip", (String[])null);
382 ChangeTask task = new JTreeChangeTask(component, key, tooltip);
383 SwingUtilities.invokeLater(task);
384 // 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!
385 ArrayList nodes = new ArrayList();
386 nodes.add(component.getModel().getRoot());
387 while(nodes.size() > 0) {
388 DictionaryTreeNode node = (DictionaryTreeNode) nodes.remove(0);
389 // Update
390 String value = get(node.getKey(), (String[])null);
391 task = new JTreeChangeTask(component, node.getKey(), node, value);
392 SwingUtilities.invokeLater(task);
393 // Add children to nodes
394 for(int i = 0; i < node.getChildCount(); i++) {
395 nodes.add(node.getChildAt(i));
396 }
397 }
398 }
399 }
400 /** Register a titled border component. */
401 public void register(TitledBorder component, String[] args, boolean already_registered) {
402 if(component != null) {
403 // Determine the key
404 String key = "";
405 if(!already_registered) {
406 key = component.getTitle();
407 }
408 else {
409 key = args[args.length - 1];
410 }
411 // Update the component using the AWTEvent queue
412 String value = get(key, args);
413 ChangeTask task = new TitledBorderChangeTask(component, key, value);
414 SwingUtilities.invokeLater(task);
415 // Register as necessary
416 if(!already_registered) {
417 args = ArrayTools.add(args, key);
418 put(component, args);
419 }
420 }
421 }
422 /** 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. */
423 private String get(String[] args, int index) {
424 return get(args[index], args);
425 }
426
427 private abstract class ChangeTask
428 implements Runnable {
429 protected String key;
430 protected String value;
431 public ChangeTask(String key, String value) {
432 this.key = key;
433 this.value = value;
434 }
435 public void run() {
436 }
437 }
438 /** Update the text and tooltip for this button. */
439 private class AbstractButtonChangeTask
440 extends ChangeTask {
441 private AbstractButton component;
442 private String tooltip;
443 public AbstractButtonChangeTask(AbstractButton component, String key, String value, String tooltip) {
444 super(key, value);
445 this.component = component;
446 this.tooltip = tooltip;
447 }
448 public void run() {
449 component.setText(value);
450 if(!tooltip.equals(key+"_Tooltip")) {
451 component.setToolTipText(tooltip);
452 }
453 else {
454 component.setToolTipText(null);
455 }
456 }
457 }
458 /** Update the text associated with a combobox. If the index used is -1 then we are setting the tooltip for this combobox. */
459 private class JComboBoxChangeTask
460 extends ChangeTask {
461 private int index;
462 private JComboBox component;
463 public JComboBoxChangeTask(JComboBox component, String key, int index, String value) {
464 super(key, value);
465 this.component = component;
466 this.index = index;
467 }
468 public void run() {
469 if(index != -1) {
470 try {
471 MutableComboBoxEntry entry = (MutableComboBoxEntry)component.getItemAt(index);
472 entry.setText(value);
473 }
474 catch (Exception error) {
475 }
476 }
477 else {
478 if(!value.equals(key+"_Tooltip")) {
479 component.setToolTipText(value);
480 }
481 else {
482 component.setToolTipText(null);
483 }
484 }
485 }
486 }
487 /** Update the title of this dialog. */
488 private class JDialogChangeTask
489 extends ChangeTask {
490 private JDialog component;
491 public JDialogChangeTask(JDialog component, String key, String value) {
492 super(key, value);
493 this.component = component;
494 }
495 public void run() {
496 component.setTitle(value);
497 }
498 }
499 /** Update the title of this frame. */
500 private class JFrameChangeTask
501 extends ChangeTask {
502 private JFrame component;
503 public JFrameChangeTask(JFrame component, String key, String value) {
504 super(key, value);
505 this.component = component;
506 }
507 public void run() {
508 component.setTitle(value);
509 }
510 }
511 /** Update the text of this label. */
512 private class JLabelChangeTask
513 extends ChangeTask {
514 private JLabel component;
515 public JLabelChangeTask(JLabel component, String key, String value) {
516 super(key, value);
517 this.component = component;
518 }
519 public void run() {
520 component.setText(value);
521 }
522 }
523 /** Updates a tabbed panes tab title and tooltip. */
524 private class JTabbedPaneChangeTask
525 extends ChangeTask {
526 private int index;
527 private JTabbedPane component;
528 private String tooltip;
529 public JTabbedPaneChangeTask(JTabbedPane component, String key, int index, String value, String tooltip) {
530 super(key, value);
531 this.component = component;
532 this.index = index;
533 this.tooltip = tooltip;
534 }
535 public void run() {
536 component.setTitleAt(index, value);
537 if(!tooltip.equals(key+"_Tooltip")) {
538 component.setToolTipTextAt(index, tooltip);
539 }
540 else {
541 component.setToolTipTextAt(index, null);
542 }
543 }
544 }
545 /** Update the text and tooltip of this text component. */
546 private class JTextComponentChangeTask
547 extends ChangeTask {
548 private JTextComponent component;
549 private String tooltip;
550 public JTextComponentChangeTask(JTextComponent component, String key, String value, String tooltip) {
551 super(key, value);
552 this.component = component;
553 this.tooltip = tooltip;
554 }
555 public void run() {
556 component.setText(value);
557 if(!tooltip.equals(key+"_Tooltip")) {
558 component.setToolTipText(tooltip);
559 }
560 else {
561 component.setToolTipText(null);
562 }
563 }
564 }
565 /** 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. */
566 private class JTreeChangeTask
567 extends ChangeTask {
568 private DictionaryTreeNode node;
569 private JTree component;
570 public JTreeChangeTask(JTree component, String key, String value) {
571 super(key, value);
572 this.component = component;
573 }
574 public JTreeChangeTask(JTree component, String key, DictionaryTreeNode node, String value) {
575 super(key, value);
576 this.component = component;
577 this.node = node;
578 }
579 public void run() {
580 if(value != null) {
581 node.setText(value);
582 ((DefaultTreeModel)component.getModel()).nodeChanged((TreeNode)node);
583 }
584 // Set the tool tip
585 else {
586 if(!value.equals(key+"_Tooltip")) {
587 component.setToolTipText(value);
588 }
589 else {
590 component.setToolTipText(null);
591 }
592 }
593 }
594 }
595 /** Update the title of this titled border. */
596 private class TitledBorderChangeTask
597 extends ChangeTask {
598 private TitledBorder component;
599 public TitledBorderChangeTask(TitledBorder component, String key, String value) {
600 super(key, value);
601 this.component = component;
602 }
603 public void run() {
604 component.setTitle(value);
605 component.getParent().repaint();
606 }
607 }
608}
609
610
611
612
Note: See TracBrowser for help on using the repository browser.