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

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

Tidied up the Dictionary a lot. Now that a restart is always required when changing dictionaries (excellent move), components do not have to be registered with the dictionary (to be updated when the dictionary changes). Registering is now the same as setting -- saving a fair bit of memory and a chunk of code.

  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 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 /** The font used when displaying various html text. */
78 static private FontUIResource font = null;
79 /** A reference to remind us of the current locale. */
80 private Locale locale = null;
81 /** The ResourceBundle which contains the raw key-value mappings. Loaded from a file named "dictionary<I>locale</I>.properties*/
82 static private ResourceBundle dictionary = null;
83
84 private TreeSet key_list = null;
85
86 /** 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.
87 * @param locale The <strong>Locale</strong> used to load the desired dictionary resource bundle.
88 */
89 public Dictionary(Locale locale, FontUIResource font) {
90 super();
91
92 if (KEY_LIST_DEBUG) {
93 // Reload the keylist
94 File file = new File(KEY_LIST_FILENAME);
95 key_list = new TreeSet();
96 try {
97 BufferedReader br = new BufferedReader(new FileReader(file));
98 String line;
99 while((line = br.readLine()) != null) {
100 key_list.add(line);
101 }
102 br.close();
103 br = null;
104 }
105 catch(Exception error) {
106 error.printStackTrace();
107 }
108 }
109
110 // Initialize.
111 this.font = font;
112 if (locale == null) {
113 this.locale = Locale.getDefault();
114 }
115 else {
116 this.locale = locale;
117 //Locale.setDefault(locale);
118 }
119 dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, this.locale);
120 // Now quickly read in language name.
121 language = dictionary.getString("Language");
122 }
123
124
125 public void destroy() {
126// if(key_list != null) {
127// try {
128// FileOutputStream fos = new FileOutputStream(KEY_LIST_FILENAME);
129// for(Iterator iter = key_list.iterator(); iter.hasNext(); ) {
130// String value = (String) iter.next();
131// fos.write(value.getBytes());
132// fos.write('\n');
133// value = null;
134// }
135// fos.close();
136// fos = null;
137// }
138// catch(Exception error) {
139// error.printStackTrace();
140// }
141// }
142 }
143
144
145 /** Retrieve the two letter code of the current language we are using, according to the stored locale.
146 * @return A <strong>String</strong> containing the two letter ISO639 language code.
147 */
148 public String getLanguage() {
149 return locale.getLanguage();
150 }
151
152
153 static public String get(String key)
154 {
155 return get(key, (String[]) null);
156 }
157
158
159 static public String get(String key, String arg)
160 {
161 String[] args = new String[1];
162 args[0] = arg;
163 return get(key, args);
164 }
165
166
167 /** 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>
168 * 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>.
169 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
170 * @param args A <strong>String[]</strong> used to populate argument fields within the complete String.
171 * @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.
172 */
173 static public String get(String key, String[] args)
174 {
175// if(key_list != null) {
176// synchronized(this) {
177// key_list.add(key);
178// }
179// }
180 try {
181 String initial = dictionary.getString(key);
182
183 // If the string contains arguments we have to insert them.
184 String complete = "";
185
186 // While we still have initial string left.
187 while(initial.length() > 0 && initial.indexOf('{') != -1 && initial.indexOf('}') != -1) {
188 // Remove preamble
189 int opening = initial.indexOf('{');
190 int closing = initial.indexOf('}');
191 int comment_mark = initial.indexOf('-', opening); // May not exist
192 if(comment_mark > closing) { // May also be detecting a later comment
193 comment_mark = -1;
194 }
195 complete = complete + initial.substring(0, opening);
196 // Parse arg_num
197 String arg_str = null;
198 if(comment_mark != -1) {
199 arg_str = initial.substring(opening + 1, comment_mark);
200 }
201 else {
202 arg_str = initial.substring(opening + 1, closing);
203 }
204 int arg_num = Integer.parseInt(arg_str);
205 if(closing + 1 < initial.length()) {
206 initial = initial.substring(closing + 1);
207 }
208 else {
209 initial = "";
210 }
211 // Insert argument
212 if(args != null && 0 <= arg_num && arg_num < args.length) {
213 complete = complete + args[arg_num];
214 }
215 else if(arg_num >= 32) {
216 String f_subargs[] = new String[1];
217 if(font != null) {
218 f_subargs[0] = font.getFontName();
219 }
220 else {
221 f_subargs[0] = "Arial";
222 }
223 complete = complete + get("Farg" + arg_num, f_subargs);
224 }
225 }
226 complete = complete + initial;
227
228 String unicode = null;
229 try {
230 unicode = new String(complete.getBytes(), "UTF-8");
231 }
232 catch (Exception ex) {
233 System.err.println("Exception: " + ex);
234 Gatherer.printStackTrace(ex);
235 return initial;
236 }
237 return unicode;
238 }
239 catch (Exception e) {
240 System.err.println("Missing value for key: " + key);
241 // Gatherer.printStackTrace(e);
242 return key;
243 }
244 }
245
246
247 static public void registerBoth(Component component, String text_key, String tooltip_key)
248 {
249 setText(component, text_key);
250 setTooltip(component, tooltip_key);
251 }
252
253
254 static public void registerText(Component component, String text_key)
255 {
256 setText(component, text_key);
257 }
258
259
260 static public void registerText(Component component, String text_key, String[] args)
261 {
262 setText(component, text_key, args);
263 }
264
265
266 static public void registerTooltip(Component component, String tooltip_key) {
267 setTooltip(component, tooltip_key);
268 }
269
270
271 static public void registerTooltipText(Component component, String tooltip) {
272 setTooltipText(component, tooltip);
273 }
274
275
276 static public void setBoth(Component component, String text_key, String tooltip_key)
277 {
278 setText(component, text_key);
279 setTooltip(component, tooltip_key);
280 }
281
282
283 static public void setText(Component component, String text_key)
284 {
285 setText(component, text_key, null);
286 }
287
288
289 static public void setText(Component component, String text_key, String[] args)
290 {
291 if (component != null) {
292 // Update the component using the AWTEvent queue
293 ComponentDetails details = new ComponentDetails(true, text_key, args, false, null);
294 ComponentUpdateTask task = new ComponentUpdateTask(component, details);
295 SwingUtilities.invokeLater(task);
296 }
297 }
298
299
300 static public void setTooltip(Component component, String tooltip_key)
301 {
302 if (component != null) {
303 // Update the component using the AWTEvent queue
304 ComponentDetails details = new ComponentDetails(false, null, null, true, tooltip_key);
305 ComponentUpdateTask task = new ComponentUpdateTask(component, details);
306 SwingUtilities.invokeLater(task);
307 }
308 }
309
310
311 static public void setTooltipText(Component component, String tooltip)
312 {
313 if (component != null) {
314 ComponentUpdateTask task = new ComponentUpdateTask(component, null, tooltip);
315 SwingUtilities.invokeLater(task);
316 }
317 }
318
319
320 static private class ComponentDetails
321 {
322 public boolean has_text;
323 public String text_key;
324 public String[] text_args;
325 public boolean has_tooltip;
326 public String tooltip_key;
327
328 public ComponentDetails(boolean has_text, String text_key, String[] text_args,
329 boolean has_tooltip, String tooltip_key)
330 {
331 this.has_text = has_text;
332 this.text_key = text_key;
333 this.text_args = text_args;
334 this.has_tooltip = has_tooltip;
335 this.tooltip_key = tooltip_key;
336 }
337 }
338
339
340 static private class ComponentUpdateTask
341 implements Runnable {
342
343 private Component component;
344 private String text = null;
345 private String tooltip = null;
346
347
348 public ComponentUpdateTask(Component component, ComponentDetails details)
349 {
350 this.component = component;
351
352 if (details.has_text) {
353 text = get(details.text_key, details.text_args);
354 }
355 if (details.has_tooltip) {
356 tooltip = get(details.tooltip_key, (String[]) null);
357 }
358 }
359
360
361 public ComponentUpdateTask(Component component, String text, String tooltip)
362 {
363 this.component = component;
364 this.text = text;
365 this.tooltip = tooltip;
366 }
367
368
369 public void run()
370 {
371 // If the component has text
372 if (text != null) {
373 if (component instanceof AbstractButton) {
374 ((AbstractButton) component).setText(text);
375 }
376 else if (component instanceof Frame) {
377 ((Frame) component).setTitle(text);
378 }
379 else if (component instanceof JDialog) {
380 ((JDialog) component).setTitle(text);
381 }
382 else if (component instanceof JLabel) {
383 ((JLabel) component).setText(text);
384 }
385 else if (component instanceof JProgressBar) {
386 ((JProgressBar) component).setString(text);
387 }
388 else if (component instanceof JTextComponent) {
389 ((JTextComponent) component).setText(text);
390 ((JTextComponent) component).setCaretPosition(0);
391 }
392 else {
393 System.err.println("Unhandled component: " + component.getClass());
394 }
395 }
396
397 // If the component has a tooltip
398 if (tooltip != null) {
399 if (component instanceof JComponent) {
400 ((JComponent) component).setToolTipText(tooltip);
401 }
402 }
403 }
404 }
405
406
407 /**
408 * Register a tab pane component. This will be deprecated eventually. */
409 static public void register(JTabbedPane component)
410 {
411 if (component != null) {
412 String[] args = new String[component.getTabCount()];
413
414 // Iterate through the tabbed panes tabs, updating values and recording the original key of each item in args.
415 for (int i = 0; i < args.length; i++) {
416 if (args[i] == null) {
417 args[i] = component.getTitleAt(i);
418 }
419 String value = get(args[i], (String[]) null);
420 String tooltip = get(args[i] + "_Tooltip", (String[]) null);
421 JTabbedPaneChangeTask task = new JTabbedPaneChangeTask(component, i, value, tooltip);
422 SwingUtilities.invokeLater(task);
423 }
424 }
425 }
426
427
428 /** Updates a tabbed panes tab title and tooltip. */
429 static private class JTabbedPaneChangeTask
430 implements Runnable {
431
432 private JTabbedPane component;
433 private int index;
434 private String value;
435 private String tooltip;
436
437 public JTabbedPaneChangeTask(JTabbedPane component, int index, String value, String tooltip) {
438 this.component = component;
439 this.index = index;
440 this.value = value;
441 this.tooltip = tooltip;
442 }
443
444 public void run() {
445 component.setTitleAt(index, value);
446 component.setToolTipTextAt(index, tooltip);
447 }
448 }
449}
Note: See TracBrowser for help on using the repository browser.