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

Last change on this file since 5986 was 5986, checked in by jmt12, 20 years ago

Arguments in the dictionary can now be annotated with more meaningful names by adding '-name' such as in {0-Collection name}

  • Property svn:keywords set to Author Date Id Revision
File size: 18.3 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 */
37package org.greenstone.gatherer;
38
39import java.awt.*;
40import java.io.*;
41import java.util.*;
42import javax.swing.*;
43import javax.swing.plaf.*;
44import javax.swing.text.*;
45import javax.swing.tree.*;
46import org.greenstone.gatherer.util.Utility;
47
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
72 static final private boolean KEY_LIST_DEBUG = false;
73 static final private String KEY_LIST_FILENAME = "keylist.txt";
74
75 /** A String which more explicitly states the Locale of this dictionary. */
76 public String language = null;
77 /** A static reference to ourself. */
78 static public Dictionary self;
79 /** The font used when displaying various html text. */
80 private FontUIResource font = null;
81 /** A reference to remind us of the current locale. */
82 private Locale locale = null;
83 /** The ResourceBundle which contains the raw key-value mappings. Loaded from a file named "dictionary<I>locale</I>.properties*/
84 private ResourceBundle dictionary = null;
85
86 private TreeSet key_list = null;;
87
88 /** 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.
89 * @param locale The <strong>Locale</strong> used to load the desired dictionary resource bundle.
90 */
91 public Dictionary(Locale locale, FontUIResource font) {
92 super();
93 this.self = this;
94
95 if (KEY_LIST_DEBUG) {
96 // Reload the keylist
97 File file = new File(KEY_LIST_FILENAME);
98 key_list = new TreeSet();
99 try {
100 BufferedReader br = new BufferedReader(new FileReader(file));
101 String line;
102 while((line = br.readLine()) != null) {
103 key_list.add(line);
104 }
105 br.close();
106 br = null;
107 }
108 catch(Exception error) {
109 error.printStackTrace();
110 }
111 }
112
113 // Initialize.
114 this.font = font;
115 if (locale == null) {
116 this.locale = Locale.getDefault();
117 }
118 else {
119 this.locale = locale;
120 Locale.setDefault(locale);
121 }
122 dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, this.locale);
123 // Now quickly read in language name.
124 language = dictionary.getString("Language");
125 }
126
127
128 /** Change the currently loaded dictionary and update registered components */
129 public void changeDictionary(Locale locale)
130 {
131 this.locale = locale;
132
133 // Load new dictionary
134 dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, locale);
135 language = dictionary.getString("Language");
136 Gatherer.println("Loaded new dictionary: " + language);
137
138 // Refresh all registered component
139 // !! TO FINISH !!
140 Gatherer.println("Updating components...");
141// ArrayList temp_list = new ArrayList();
142// Iterator components = keySet().iterator();
143// while (components.hasNext()) {
144// Component component = (Component) components.next();
145// temp_list.add(component);
146// }
147
148// for (int i = 0; i < temp_list.size(); i++) {
149// Component component = (Component) temp_list.get(i);
150// ComponentDetails details = (ComponentDetails) get(component);
151// oldSetBoth(component, details.text_key, details.text_args, details.tooltip_key);
152// }
153
154// for (Iterator keys = keySet().iterator(); keys.hasNext(); ) {
155// Component component = (Component) keys.next();
156// ComponentDetails details = (ComponentDetails) get(component);
157// oldSetBoth(component, details.text_key, details.text_args, details.tooltip_key);
158// }
159 }
160
161
162 /** Change the currently loaded dictionary and update registered (ie dynamic) components as possible. */
163// public void oldChangeDictionary(Locale locale) {
164// this.locale = locale;
165// // Load new dictionary
166// dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, locale);
167// language = dictionary.getString("Language");
168// Gatherer.println("Having loaded new dictionary: " + language);
169// // Refresh all registered component
170// Gatherer.println("Updating components");
171// for(Iterator keys = keySet().iterator(); keys.hasNext(); ) {
172// Object component = keys.next();
173// String[] args = (String[]) get(component);
174// if(component instanceof AbstractButton) {
175// register((AbstractButton)component, args, true);
176// }
177// else if(component instanceof JComboBox) {
178// register((JComboBox)component, args, true);
179// }
180// else if(component instanceof JDialog) {
181// register((JDialog)component, args, true);
182// }
183// else if(component instanceof JFrame) {
184// register((JFrame)component, args, true);
185// }
186// else if(component instanceof JLabel) {
187// register((JLabel)component, args, true);
188// }
189// else if(component instanceof JTabbedPane) {
190// register((JTabbedPane)component, args, true);
191// }
192// else if(component instanceof JTextComponent) {
193// register((JTextComponent)component, args, true);
194// }
195// else if(component instanceof JTree) {
196// register((JTree)component, args, true);
197// }
198// else if(component instanceof TitledBorder) {
199// register((TitledBorder)component, args, true);
200// }
201// args = null;
202// component = null;
203// }
204// }
205
206 /** Remove the component from our registered components list. */
207 /* private void deregister(Object component) {
208 remove(component);
209 } */
210
211 public void destroy() {
212 if(key_list != null) {
213 try {
214 FileOutputStream fos = new FileOutputStream(KEY_LIST_FILENAME);
215 for(Iterator iter = key_list.iterator(); iter.hasNext(); ) {
216 String value = (String) iter.next();
217 fos.write(value.getBytes());
218 fos.write('\n');
219 value = null;
220 }
221 fos.close();
222 fos = null;
223 }
224 catch(Exception error) {
225 error.printStackTrace();
226 }
227 }
228 }
229
230
231 static public String get(String key)
232 {
233 return self.oldget(key);
234 }
235
236
237 static public String get(String key, String arg)
238 {
239 String[] args = new String[1];
240 args[0] = arg;
241 return self.oldget(key, args);
242 }
243
244
245 static public String get(String key, String[] args)
246 {
247 return self.oldget(key, args);
248 }
249
250
251 /**
252 * @deprecated
253 * Overloaded to call get with both a key and an empty argument array.
254 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
255 * @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.
256 */
257 public String oldget(String key) {
258 return oldget(key, (String[])null);
259 }
260
261 /**
262 * @deprecated
263 * Convienence method with transforms the second string argument into a string array. */
264 public String oldget(String key, String arg) {
265 String[] args = new String[1];
266 args[0] = arg;
267 return oldget(key, args);
268 }
269
270 /** 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>
271 * 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>.
272 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
273 * @param args A <strong>String[]</strong> used to populate argument fields within the complete String.
274 * @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.
275 */
276 public String oldget(String key, String args[]) {
277 if(key_list != null) {
278 synchronized(this) {
279 key_list.add(key);
280 }
281 }
282 try {
283 String initial = dictionary.getString(key);
284 // If the string contains arguments we have to insert them.
285 String complete = "";
286 // While we still have initial string left.
287 while(initial.length() > 0 && initial.indexOf('{') != -1 && initial.indexOf('}') != -1) {
288 // Remove preamble
289 int opening = initial.indexOf('{');
290 int closing = initial.indexOf('}');
291 int comment_mark = initial.indexOf('-', opening); // May not exist
292 if(comment_mark > closing) { // May also be detecting a later comment
293 comment_mark = -1;
294 }
295 complete = complete + initial.substring(0, opening);
296 // Parse arg_num
297 String arg_str = null;
298 if(comment_mark != -1) {
299 arg_str = initial.substring(opening + 1, comment_mark);
300 }
301 else {
302 arg_str = initial.substring(opening + 1, closing);
303 }
304 int arg_num = Integer.parseInt(arg_str);
305 if(closing + 1 < initial.length()) {
306 initial = initial.substring(closing + 1);
307 }
308 else {
309 initial = "";
310 }
311 // Insert argument
312 if(args != null && 0 <= arg_num && arg_num < args.length) {
313 complete = complete + args[arg_num];
314 }
315 else if(arg_num >= 32) {
316 String f_subargs[] = new String[1];
317 if(font != null) {
318 f_subargs[0] = font.getFontName();
319 }
320 else {
321 f_subargs[0] = "Arial";
322 }
323 complete = complete + oldget("Farg" + arg_num, f_subargs);
324 }
325 }
326 return complete + initial;
327 }
328 catch (Exception e) {
329 System.err.println("Missing value for key: " + key);
330 Gatherer.printStackTrace(e);
331 return key;
332 }
333 }
334
335 /** Retrieve the two letter code of the current language we are using, according to the stored locale.
336 * @return A <strong>String</strong> containing the two letter ISO639 language code.
337 */
338 public String getLanguage() {
339 return locale.getLanguage();
340 }
341
342
343 static public void setBoth(Component component, String text_key, String tooltip_key)
344 {
345 setText(component, text_key);
346 setTooltip(component, tooltip_key);
347 }
348
349
350 static public void setText(Component component, String text_key)
351 {
352 setText(component, text_key, null);
353 }
354
355
356 static public void setText(Component component, String text_key, String[] args)
357 {
358 if (component != null) {
359 // Update the component using the AWTEvent queue
360 ComponentDetails details = new ComponentDetails(true, text_key, args, false, null);
361 ComponentUpdateTask task = new ComponentUpdateTask(component, details, true);
362 SwingUtilities.invokeLater(task);
363 }
364 }
365
366
367 static public void setTooltip(Component component, String tooltip_key)
368 {
369 if (component != null) {
370 // Update the component using the AWTEvent queue
371 ComponentDetails details = new ComponentDetails(false, null, null, true, tooltip_key);
372 ComponentUpdateTask task = new ComponentUpdateTask(component, details, true);
373 SwingUtilities.invokeLater(task);
374 }
375 }
376
377
378 static public void registerBoth(Component component, String text_key, String tooltip_key)
379 {
380 registerText(component, text_key, null);
381 registerTooltip(component, tooltip_key);
382 }
383
384
385 static public void registerText(Component component, String text_key)
386 {
387 registerText(component, text_key, null);
388 }
389
390
391 static public void registerText(Component component, String text_key, String[] args)
392 {
393 if (component != null) {
394 // Update the component's details
395 ComponentDetails details = (ComponentDetails) self.remove(component);
396 if (details == null) {
397 details = new ComponentDetails(true, text_key, args, false, null);
398 }
399 else {
400 details.has_text = true;
401 details.text_key = text_key;
402 details.text_args = args;
403 }
404 self.put(component, details);
405
406 // Update the component using the AWTEvent queue
407 ComponentUpdateTask task = new ComponentUpdateTask(component, details, true);
408 SwingUtilities.invokeLater(task);
409 }
410 }
411
412 static public void registerTooltip(Component component, String tooltip_key) {
413 self.registerTooltip(component, tooltip_key, true);
414 }
415
416 static public void registerTooltip(Component component, String tooltip_key, boolean is_key)
417 {
418 if (component != null) {
419 // Update the component's details
420 ComponentDetails details = (ComponentDetails) self.remove(component);
421 if (details == null) {
422 details = new ComponentDetails(false, null, null, true, tooltip_key);
423 }
424 else {
425 details.has_tooltip = true;
426 details.tooltip_key = tooltip_key;
427 }
428 self.put(component, details);
429
430 // Update the component using the AWTEvent queue
431 ComponentUpdateTask task = new ComponentUpdateTask(component, details, is_key);
432 SwingUtilities.invokeLater(task);
433 }
434 }
435
436
437 static private class ComponentDetails
438 {
439 public boolean has_text;
440 public String text_key;
441 public String[] text_args;
442 public boolean has_tooltip;
443 public String tooltip_key;
444
445 public ComponentDetails(boolean has_text, String text_key, String[] text_args,
446 boolean has_tooltip, String tooltip_key)
447 {
448 this.has_text = has_text;
449 this.text_key = text_key;
450 this.text_args = text_args;
451 this.has_tooltip = has_tooltip;
452 this.tooltip_key = tooltip_key;
453 }
454 }
455
456
457 static private class ComponentUpdateTask
458 implements Runnable {
459
460 private Component component;
461 private String text = null;
462 private String tooltip = null;
463
464 public ComponentUpdateTask(Component component, ComponentDetails details, boolean is_key)
465 {
466 this.component = component;
467
468 if (details.has_text) {
469 text = self.oldget(details.text_key, details.text_args);
470 }
471 if (details.has_tooltip) {
472 if(is_key) {
473 tooltip = self.oldget(details.tooltip_key, (String[]) null);
474 }
475 else {
476 tooltip = details.tooltip_key;
477 }
478 }
479 }
480
481
482 public void run()
483 {
484 // If the component has text
485 if (text != null) {
486 if (component instanceof AbstractButton) {
487 ((AbstractButton) component).setText(text);
488 }
489 else if (component instanceof Frame) {
490 ((Frame) component).setTitle(text);
491 }
492 else if (component instanceof JDialog) {
493 ((JDialog) component).setTitle(text);
494 }
495 else if (component instanceof JLabel) {
496 ((JLabel) component).setText(text);
497 }
498 else if (component instanceof JProgressBar) {
499 ((JProgressBar) component).setString(text);
500 }
501 else if (component instanceof JTextComponent) {
502 ((JTextComponent) component).setText(text);
503 ((JTextComponent) component).setCaretPosition(0);
504 }
505 else {
506 System.err.println("Unhandled component: " + component.getClass());
507 }
508 }
509
510 // If the component has a tooltip
511 if (tooltip != null) {
512 if (component instanceof JComponent) {
513 ((JComponent) component).setToolTipText(tooltip);
514 }
515 }
516 }
517 }
518
519
520 /**
521 * Register a tab pane component. This will be deprecated eventually. */
522 static public void register(JTabbedPane component)
523 {
524 if (component != null) {
525 String[] args = new String[component.getTabCount()];
526
527 // Iterate through the tabbed panes tabs, updating values and recording the original key of each item in args.
528 for (int i = 0; i < args.length; i++) {
529 if (args[i] == null) {
530 args[i] = component.getTitleAt(i);
531 }
532 String value = self.oldget(args[i], (String[]) null);
533 String tooltip = self.oldget(args[i] + "_Tooltip", (String[]) null);
534 JTabbedPaneChangeTask task = new JTabbedPaneChangeTask(component, i, value, tooltip);
535 SwingUtilities.invokeLater(task);
536 }
537 }
538 }
539
540
541 /** Updates a tabbed panes tab title and tooltip. */
542 static private class JTabbedPaneChangeTask
543 implements Runnable {
544
545 private JTabbedPane component;
546 private int index;
547 private String value;
548 private String tooltip;
549
550 public JTabbedPaneChangeTask(JTabbedPane component, int index, String value, String tooltip) {
551 this.component = component;
552 this.index = index;
553 this.value = value;
554 this.tooltip = tooltip;
555 }
556
557 public void run() {
558 component.setTitleAt(index, value);
559 component.setToolTipTextAt(index, tooltip);
560 }
561 }
562}
Note: See TracBrowser for help on using the repository browser.