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

Last change on this file since 6573 was 6573, checked in by mdewsnip, 20 years ago

Added code to treat strings from the dictionary as UTF-8.

  • Property svn:keywords set to Author Date Id Revision
File size: 18.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 */
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
285 // If the string contains arguments we have to insert them.
286 String complete = "";
287 // While we still have initial string left.
288 while(initial.length() > 0 && initial.indexOf('{') != -1 && initial.indexOf('}') != -1) {
289 // Remove preamble
290 int opening = initial.indexOf('{');
291 int closing = initial.indexOf('}');
292 int comment_mark = initial.indexOf('-', opening); // May not exist
293 if(comment_mark > closing) { // May also be detecting a later comment
294 comment_mark = -1;
295 }
296 complete = complete + initial.substring(0, opening);
297 // Parse arg_num
298 String arg_str = null;
299 if(comment_mark != -1) {
300 arg_str = initial.substring(opening + 1, comment_mark);
301 }
302 else {
303 arg_str = initial.substring(opening + 1, closing);
304 }
305 int arg_num = Integer.parseInt(arg_str);
306 if(closing + 1 < initial.length()) {
307 initial = initial.substring(closing + 1);
308 }
309 else {
310 initial = "";
311 }
312 // Insert argument
313 if(args != null && 0 <= arg_num && arg_num < args.length) {
314 complete = complete + args[arg_num];
315 }
316 else if(arg_num >= 32) {
317 String f_subargs[] = new String[1];
318 if(font != null) {
319 f_subargs[0] = font.getFontName();
320 }
321 else {
322 f_subargs[0] = "Arial";
323 }
324 complete = complete + oldget("Farg" + arg_num, f_subargs);
325 }
326 }
327 complete = complete + initial;
328
329 String unicode = null;
330 try {
331 unicode = new String(complete.getBytes(), "UTF-8");
332 }
333 catch (Exception ex) {
334 System.err.println("Something bad. " + ex);
335 Gatherer.printStackTrace(ex);
336 return initial;
337 }
338 return unicode;
339 }
340 catch (Exception e) {
341 System.err.println("Missing value for key: " + key);
342 Gatherer.printStackTrace(e);
343 return key;
344 }
345 }
346
347 /** Retrieve the two letter code of the current language we are using, according to the stored locale.
348 * @return A <strong>String</strong> containing the two letter ISO639 language code.
349 */
350 public String getLanguage() {
351 return locale.getLanguage();
352 }
353
354
355 static public void setBoth(Component component, String text_key, String tooltip_key)
356 {
357 setText(component, text_key);
358 setTooltip(component, tooltip_key);
359 }
360
361
362 static public void setText(Component component, String text_key)
363 {
364 setText(component, text_key, null);
365 }
366
367
368 static public void setText(Component component, String text_key, String[] args)
369 {
370 if (component != null) {
371 // Update the component using the AWTEvent queue
372 ComponentDetails details = new ComponentDetails(true, text_key, args, false, null);
373 ComponentUpdateTask task = new ComponentUpdateTask(component, details, true);
374 SwingUtilities.invokeLater(task);
375 }
376 }
377
378
379 static public void setTooltip(Component component, String tooltip_key)
380 {
381 if (component != null) {
382 // Update the component using the AWTEvent queue
383 ComponentDetails details = new ComponentDetails(false, null, null, true, tooltip_key);
384 ComponentUpdateTask task = new ComponentUpdateTask(component, details, true);
385 SwingUtilities.invokeLater(task);
386 }
387 }
388
389
390 static public void registerBoth(Component component, String text_key, String tooltip_key)
391 {
392 registerText(component, text_key, null);
393 registerTooltip(component, tooltip_key);
394 }
395
396
397 static public void registerText(Component component, String text_key)
398 {
399 registerText(component, text_key, null);
400 }
401
402
403 static public void registerText(Component component, String text_key, String[] args)
404 {
405 if (component != null) {
406 // Update the component's details
407 ComponentDetails details = (ComponentDetails) self.remove(component);
408 if (details == null) {
409 details = new ComponentDetails(true, text_key, args, false, null);
410 }
411 else {
412 details.has_text = true;
413 details.text_key = text_key;
414 details.text_args = args;
415 }
416 self.put(component, details);
417
418 // Update the component using the AWTEvent queue
419 ComponentUpdateTask task = new ComponentUpdateTask(component, details, true);
420 SwingUtilities.invokeLater(task);
421 }
422 }
423
424 static public void registerTooltip(Component component, String tooltip_key) {
425 self.registerTooltip(component, tooltip_key, true);
426 }
427
428 static public void registerTooltip(Component component, String tooltip_key, boolean is_key)
429 {
430 if (component != null) {
431 // Update the component's details
432 ComponentDetails details = (ComponentDetails) self.remove(component);
433 if (details == null) {
434 details = new ComponentDetails(false, null, null, true, tooltip_key);
435 }
436 else {
437 details.has_tooltip = true;
438 details.tooltip_key = tooltip_key;
439 }
440 self.put(component, details);
441
442 // Update the component using the AWTEvent queue
443 ComponentUpdateTask task = new ComponentUpdateTask(component, details, is_key);
444 SwingUtilities.invokeLater(task);
445 }
446 }
447
448 static public void unregister(Component component) {
449 self.remove(component);
450 }
451
452
453 static private class ComponentDetails
454 {
455 public boolean has_text;
456 public String text_key;
457 public String[] text_args;
458 public boolean has_tooltip;
459 public String tooltip_key;
460
461 public ComponentDetails(boolean has_text, String text_key, String[] text_args,
462 boolean has_tooltip, String tooltip_key)
463 {
464 this.has_text = has_text;
465 this.text_key = text_key;
466 this.text_args = text_args;
467 this.has_tooltip = has_tooltip;
468 this.tooltip_key = tooltip_key;
469 }
470 }
471
472
473 static private class ComponentUpdateTask
474 implements Runnable {
475
476 private Component component;
477 private String text = null;
478 private String tooltip = null;
479
480 public ComponentUpdateTask(Component component, ComponentDetails details, boolean is_key)
481 {
482 this.component = component;
483
484 if (details.has_text) {
485 text = self.oldget(details.text_key, details.text_args);
486 }
487 if (details.has_tooltip) {
488 if(is_key) {
489 tooltip = self.oldget(details.tooltip_key, (String[]) null);
490 }
491 else {
492 tooltip = details.tooltip_key;
493 }
494 }
495 }
496
497
498 public void run()
499 {
500 // If the component has text
501 if (text != null) {
502 if (component instanceof AbstractButton) {
503 ((AbstractButton) component).setText(text);
504 }
505 else if (component instanceof Frame) {
506 ((Frame) component).setTitle(text);
507 }
508 else if (component instanceof JDialog) {
509 ((JDialog) component).setTitle(text);
510 }
511 else if (component instanceof JLabel) {
512 ((JLabel) component).setText(text);
513 }
514 else if (component instanceof JProgressBar) {
515 ((JProgressBar) component).setString(text);
516 }
517 else if (component instanceof JTextComponent) {
518 ((JTextComponent) component).setText(text);
519 ((JTextComponent) component).setCaretPosition(0);
520 }
521 else {
522 System.err.println("Unhandled component: " + component.getClass());
523 }
524 }
525
526 // If the component has a tooltip
527 if (tooltip != null) {
528 if (component instanceof JComponent) {
529 ((JComponent) component).setToolTipText(tooltip);
530 }
531 }
532 }
533 }
534
535
536 /**
537 * Register a tab pane component. This will be deprecated eventually. */
538 static public void register(JTabbedPane component)
539 {
540 if (component != null) {
541 String[] args = new String[component.getTabCount()];
542
543 // Iterate through the tabbed panes tabs, updating values and recording the original key of each item in args.
544 for (int i = 0; i < args.length; i++) {
545 if (args[i] == null) {
546 args[i] = component.getTitleAt(i);
547 }
548 String value = self.oldget(args[i], (String[]) null);
549 String tooltip = self.oldget(args[i] + "_Tooltip", (String[]) null);
550 JTabbedPaneChangeTask task = new JTabbedPaneChangeTask(component, i, value, tooltip);
551 SwingUtilities.invokeLater(task);
552 }
553 }
554 }
555
556
557 /** Updates a tabbed panes tab title and tooltip. */
558 static private class JTabbedPaneChangeTask
559 implements Runnable {
560
561 private JTabbedPane component;
562 private int index;
563 private String value;
564 private String tooltip;
565
566 public JTabbedPaneChangeTask(JTabbedPane component, int index, String value, String tooltip) {
567 this.component = component;
568 this.index = index;
569 this.value = value;
570 this.tooltip = tooltip;
571 }
572
573 public void run() {
574 component.setTitleAt(index, value);
575 component.setToolTipTextAt(index, tooltip);
576 }
577 }
578}
Note: See TracBrowser for help on using the repository browser.