1/*
2 *****************************************************************************
3 * Copyright (C) 2000-2007, International Business Machines Corporation and  *
4 * others. All Rights Reserved.                                              *
5 *****************************************************************************
6 */
7package com.ibm.rbm;
8
9import java.util.*;
10import java.io.*;
11import javax.swing.UIManager;
12import javax.swing.JOptionPane;
13
14import com.ibm.rbm.gui.RBManagerGUI;
15
16/**
17 * A utility class to aid in the process of updating the Natural Language Support of Tempus Fugit.
18 * This class scans the directory containing NLS files and checks the various languages found there
19 * for completeness, duplication of entry, and status of translation. The class can be instantiated
20 * through a constructor, or it can be run from the command line. For additional information on the
21 * command line results, see the <CODE>main</CODE> method.
22 *
23 * @author Jared Jackson
24 * @see com.ibm.rbm.RBManager
25 */
26public class RBManager {
27
28    // *** DATA ***
29    private Vector allBundleKeys;                               // A Vector of Strings with all defined NLS properties
30    private Vector bundles;                                   // A Vector of NLSbundles, one for each language
31    private String currentUser;                                 // The name of the person currently using the editor
32    private String baseClass;                                   // The name of the base class of the active resource bundle
33    private File   currentDirectory;
34
35    // *** CONSTRUCTORS ***
36
37    // The default constructor is not publicly available
38    private RBManager() {
39        try {
40            // Look and Feel check
41            try {
42                String laf = Preferences.getPreference("lookandfeel");
43                if (!laf.equals("")) UIManager.setLookAndFeel(laf);
44            } catch (Exception e) {
45                // Ignored
46            }
47
48            Resources.initBundle();
49            RBManagerGUI guiFrame = new RBManagerGUI();
50            if (!Preferences.getPreference("username").equals(""))
51                guiFrame.setUser(Preferences.getPreference("username"));
52            if (!Preferences.getPreference("locale").equals("")) {
53                String localeStr = Preferences.getPreference("locale");
54                String language = Resources.getLanguage(localeStr);
55                String country = Resources.getCountry(localeStr);
56                String variant = Resources.getVariant(localeStr);
57                if (language == null || language.equals("") || language.length() > 3) language = "en";
58                if (country == null) country = new String();
59                if (variant == null) Resources.setLocale(new Locale(language, country));
60                else Resources.setLocale(new Locale(language, country, variant));
61            }
62            Resources.initBundle();
63            guiFrame.initComponents();
64            guiFrame.setVisible(true);
65        } catch (Exception e) {
66            e.printStackTrace();
67        }
68    }
69
70    /**
71     * This constructor creates an entirely blank RBManager and base Bundle. Only the base class name is defined.
72     * All other properties need to be defined.
73     */
74
75    public RBManager(String baseClassName) {
76        allBundleKeys = new Vector();
77        bundles = new Vector();
78        currentUser = "Unknown";
79        baseClass = baseClassName;
80        currentDirectory = new File("");
81
82        Bundle mainBundle = new Bundle("");
83        // Create a default group
84        mainBundle.addBundleGroup("Ungrouped Items", "These are resource items that have not been assigned a group");
85        bundles.addElement(mainBundle);
86    }
87
88    /**
89     * This is the standard constructor for RBManager. It is constructed from the root of a resource bundle.
90     * In the current implementation, each file is parsed separately starting with the base class file (root).
91     * In this implementation, the lookup keys are represented to the user as they appear in the files. The
92     * translation values however are translated according to the basic rules defined in java.util.Properties.
93     * Thus in the key, the user may see '\"' when in the value it would have been converted to '"'. This
94     * translation is reversed when saving the resource bundle.
95     * @param mainFile The base class file of the resource bundle to be read
96     */
97
98    public RBManager(File mainFile) throws FileNotFoundException, IOException {
99        init();
100
101        currentDirectory = new File(mainFile.getParent());
102
103        String[] encodings;
104
105        // Initialize the readers to the main NLS file
106        FileReader fr = new FileReader(mainFile);
107        BufferedReader br = new BufferedReader(fr);
108
109        // Load the java readable values from the main NLS file;
110        Properties p = new Properties();
111        p.load(new FileInputStream(mainFile));
112
113        // Count the number of language files and set up the encoding and dictionary data
114        int numLanguages = 1;
115        String NLSbaseClass = null;
116        String NLSpostfix   = null;
117
118        if (mainFile.getName().indexOf(".") >= 0) {
119            NLSbaseClass = mainFile.getName().substring(0,mainFile.getName().indexOf("."));
120            NLSpostfix = ".properties";
121        } else {
122            NLSbaseClass = mainFile.getName();
123            NLSpostfix = "";
124        }
125
126        baseClass = NLSbaseClass;
127
128        String filePrefix = mainFile.getName().substring(0,mainFile.getName().lastIndexOf("."));
129        String filePostfix = mainFile.getName().substring(mainFile.getName().lastIndexOf("."),mainFile.getName().length());
130        File resDir = currentDirectory;
131        if (resDir != null && resDir.isDirectory()) {
132            String[] temp = resDir.list();
133            numLanguages = 0;
134            // Count the number of language files
135            for (int i = 0; i < temp.length; i++) {
136                if (temp[i].startsWith(NLSbaseClass) && (temp[i].endsWith(NLSpostfix)
137                    || temp[i].endsWith(NLSpostfix.toUpperCase()) || NLSpostfix.equals(""))) {
138                    // Starts with the base class name and ends in proper suffix (above)
139                    // Base name is followed by . or _ (below)
140                    RBManagerGUI.debugMsg("Character is: " + temp[i].charAt(NLSbaseClass.length()));
141                    if (temp[i].charAt(NLSbaseClass.length()) == '.' || temp[i].charAt(NLSbaseClass.length()) == '_')
142                        numLanguages++;
143                }
144            }
145            // Initialize the bundles and encodings
146            encodings = new String[numLanguages];
147
148            int count = 1;
149            for (int i = 0; i < temp.length; i++) {
150                if (temp[i].equals(mainFile.getName())) {
151                    encodings[0] = "";
152                } else if (temp[i].startsWith(NLSbaseClass) && (temp[i].endsWith(NLSpostfix)
153                    || temp[i].endsWith(NLSpostfix.toUpperCase()) || NLSpostfix.equals(""))) {
154                    if (temp[i].charAt(NLSbaseClass.length()) == '.' || temp[i].charAt(NLSbaseClass.length()) == '_') {
155                        encodings[count] = new String(temp[i].substring(filePrefix.length()+1,temp[i].indexOf(filePostfix)));							count++;
156                    }
157                }
158            }
159        } else {
160            // Initialize the bundles and encodings in case the directory information is not available
161            // In this case, only the main NLS file will be handled
162            encodings = new String[numLanguages];
163            encodings[0] = new String("");
164        } // end the count and initialization
165
166        // Read in the entries from the main file
167        String line;
168        // Set the dictionary for the main file
169        Bundle dict = new Bundle(encodings[0]);
170        bundles.addElement(dict);
171        // Set up the first group in case there are NLS items which were not assigned to a group
172        BundleGroup group = new BundleGroup(dict, "Ungrouped Items");
173        group.setComment("NLS Items which were not initially assigned to a group");
174        dict.addBundleGroup(group);
175        BundleItem item = new BundleItem(group,null,null);
176        int count = 0;
177        while ((line = br.readLine()) != null) {
178            // Test to make sure this is a file that was generated by RBManager
179            if (!line.trim().equals("")) count++;
180            if (count == 1 && !line.startsWith("# @file")) {
181                // Not generated by RBManager
182                JOptionPane.showMessageDialog(null,
183                    Resources.getTranslation("error_not_rbmanager_format") + "\n" + Resources.getTranslation("error_suggest_import_properties"),
184                    Resources.getTranslation("dialog_title_error_not_rbmanager_format"), JOptionPane.ERROR_MESSAGE);
185                throw new FileNotFoundException("Improper format for file: " + mainFile.getName());
186            }
187            String commentLine = null;
188            // Grab text following the # sign
189            if (line.indexOf("#") >= 0) {
190                commentLine = line.substring(line.indexOf("#")+1,line.length());
191                line = line.substring(0,line.indexOf("#"));
192            }
193            if (commentLine != null && commentLine.trim().length() > 0) {
194                // Process any information made available in comment '@' information
195                Hashtable descriptors = getDescriptors(null,commentLine);
196                if (descriptors != null) {
197                    Object o;
198                    // File tags
199                    o = descriptors.get("file"); if (o != null) dict.name = ((String) o);
200                    o = descriptors.get("fileComment");  if (o != null) dict.comment  = ((String) o);
201                    o = descriptors.get("fileLanguage"); if (o != null) dict.language = ((String) o);
202                    o = descriptors.get("fileCountry");  if (o != null) dict.country  = ((String) o);
203                    o = descriptors.get("fileVariant");  if (o != null) dict.variant  = ((String) o);
204                    o = descriptors.get("fileManager");  if (o != null) dict.manager  = ((String) o);
205
206                    // Group tags
207                    o = descriptors.get("group");
208                    if (o != null) {
209                        group = new BundleGroup(dict, (String)o);
210                        item.setParentGroup(group);
211                        dict.addBundleGroup(group);
212                    }
213                    o = descriptors.get("groupComment"); if (o != null) group.setComment((String) o);
214
215                    // Item tags
216                    o = descriptors.get("comment");    if (o != null) item.setComment((String) o);
217                    o = descriptors.get("translated"); if (o != null) item.setTranslated(((String) o).equalsIgnoreCase("true"));
218                    o = descriptors.get("creator");    if (o != null) item.setCreator((String) o);
219                    o = descriptors.get("modifier");   if (o != null) item.setModifier((String) o);
220                    o = descriptors.get("created");    if (o != null) item.setCreatedDate((String) o);
221                    o = descriptors.get("modified");   if (o != null) item.setModifiedDate((String) o);
222
223                    // Lookup tags (e.g. {_#_} _description_)
224                    Enumeration keys = descriptors.keys();
225                    while (keys.hasMoreElements()) {
226                        String tag = (String)keys.nextElement();
227                        if (tag.startsWith("{")) {
228                            if (tag.indexOf("}") < 0) continue;
229                            String lookup = tag.substring(1,tag.indexOf("}"));
230                            item.getLookups().put(lookup, descriptors.get(tag));
231                        }
232                    }
233                }
234            } // end check of comment line
235            if (line.trim().length() < 1) continue;
236
237            // Grab the name and value (translation) from the line
238            int breakpoint = 0;
239            boolean started = false;
240            char array[] = line.toCharArray();
241            for (int i=0; i < array.length; i++) {
242                if (!started && array[i] != ' ' && array[i] != '\t') started = true;
243                if (started && (array[i] == '=' || array[i] == ':' || array[i] == ' ' || array[i] == '\t')) {
244                    breakpoint = i;
245                    break;
246                }
247            }
248            String key = String.valueOf(array,0,breakpoint);
249
250            item.setKey(key);
251            String translation = p.getProperty(key);
252            if (translation == null || translation.equals(""))
253                item.setTranslation(line.substring(line.indexOf("=")+1,line.length()).trim());
254            else item.setTranslation(translation);
255
256            dict.addBundleItem(item);
257            item = new BundleItem(group,null,null);
258        } // end while - main NLS file
259
260        // Now that we have parsed the entire main language file, populate the allNLSKey set with the dictionary keys
261        allBundleKeys = new Vector();
262        Enumeration keys = ((Bundle)bundles.elementAt(0)).allItems.keys();
263        while (keys.hasMoreElements()) {
264            allBundleKeys.addElement(keys.nextElement());
265        }
266
267        // Now go through all of the other languages
268        for (int i = 1; i < encodings.length; i++) {
269            if (encodings[i].equals("kr")) continue; // I can't handle double byte character sets yet
270            // Try to obtain the new file
271            File tempFile = new File(resDir, NLSbaseClass + "_" + encodings[i] + NLSpostfix);
272            fr = new FileReader(tempFile);
273            br = new BufferedReader(fr);
274
275            // Try to obtain the java readable properties for the file
276            p = new Properties();
277            p.load(new FileInputStream(tempFile));
278
279            // Set the dictionary for the main file
280            dict = new Bundle(encodings[i]);
281            bundles.addElement(dict);
282            // Set up the first group in case there are NLS items which were not assigned to a group
283            group = new BundleGroup(dict, "Ungrouped Items");
284            dict.addBundleGroup(group);
285            group.setComment("NLS Items which were not initially assigned to a group");
286            item = new BundleItem(group,null,null);
287            // Create the rest of the groups
288            while ((line = br.readLine()) != null) {
289                String commentLine = null;
290                // Grab the text following the # sign
291                if (line.indexOf("#") >= 0) {
292                    commentLine = line.substring(line.indexOf("#")+1,line.length());
293                    line = line.substring(0,line.indexOf("#"));
294                }
295                if (commentLine != null && commentLine.trim().length() > 0) {
296                    // Process any information made available in comment '@' information
297                    Hashtable descriptors = getDescriptors(null,commentLine);
298                    if (descriptors != null) {
299                        Object o;
300                        // File tags
301                        o = descriptors.get("file"); if (o != null) dict.name = ((String) o);
302                        o = descriptors.get("fileComment");  if (o != null) dict.comment  = ((String) o);
303                        o = descriptors.get("fileLanguage"); if (o != null) dict.language = ((String) o);
304                        o = descriptors.get("fileCountry");  if (o != null) dict.country  = ((String) o);
305                        o = descriptors.get("fileVariant");  if (o != null) dict.variant  = ((String) o);
306                        o = descriptors.get("fileManager");  if (o != null) dict.manager  = ((String) o);
307
308                        // Group tags
309                        o = descriptors.get("group");
310                        if (o != null) {
311                            group = new BundleGroup(dict, (String)o);
312                            item.setParentGroup(group);
313                            dict.addBundleGroup(group);
314                        }
315                        o = descriptors.get("groupComment"); if (o != null) group.setComment((String) o);
316
317                        // Item tags
318                        o = descriptors.get("comment");    if (o != null) item.setComment((String) o);
319                        o = descriptors.get("translated"); if (o != null) item.setTranslated(((String) o).equalsIgnoreCase("true"));
320                        o = descriptors.get("creator");    if (o != null) item.setCreator((String) o);
321                        o = descriptors.get("modifier");   if (o != null) item.setModifier((String) o);
322                        o = descriptors.get("created");    if (o != null) item.setCreatedDate((String) o);
323                        o = descriptors.get("modified");   if (o != null) item.setModifiedDate((String) o);
324
325                        // Lookup tags (e.g. {_#_} _description_)
326                        Enumeration descKeys = descriptors.keys();
327                        while (descKeys.hasMoreElements()) {
328                            String tag = (String)descKeys.nextElement();
329                            if (tag.startsWith("{")) {
330                                if (tag.indexOf("}") < 0) continue;
331                                String lookup = tag.substring(1,tag.indexOf("}"));
332                                item.getLookups().put(lookup, descriptors.get(tag));
333                            }
334                        }
335                    }
336                } // end check of comment line
337                if (line.trim().length() < 1) continue;
338
339                // Grab the name and value (translation) from the line
340                int breakpoint = 0;
341                boolean started = false;
342                char array[] = line.toCharArray();
343                for (int j=0; j < array.length; j++) {
344                    if (!started && array[j] != ' ' && array[j] != '\t') started = true;
345                    if (started && (array[j] == '=' || array[j] == ':' || array[j] == ' ' || array[j] == '\t')) {
346                        breakpoint = j;
347                        break;
348                    }
349                }
350                String key = String.valueOf(array,0,breakpoint);
351                item.setKey(key);
352                String translation = p.getProperty(key);
353                if (translation == null || translation.equals(""))
354                    item.setTranslation(line.substring(line.indexOf("=")+1,line.length()).trim());
355                else item.setTranslation(translation);
356
357                dict.addBundleItem(item);
358                item = new BundleItem(group,null,null);
359            } // end while - next line
360        } // end for looop through languages
361        // Add this opened file to our recent files
362        Preferences.addRecentFilePreference(mainFile.getName(), mainFile.getAbsolutePath());
363    } // end RBManager()
364
365    // *** METHODS ***
366
367    /**
368     * Main
369     */
370
371    public static void main(String args[]) {
372        // Make sure the user specified a path
373        if (args.length < 1) {
374            new RBManager();
375            return;
376        }
377    } // main
378
379    public String toString() { return baseClass; }
380
381    /**
382     * Write the contents of the file to the output stream
383     */
384
385    public void writeToFile() throws IOException {
386        for (int i = 0; i < bundles.size(); i++) {
387            Bundle bundle = (Bundle)bundles.elementAt(i);
388            File outputFile = new File(currentDirectory, baseClass +
389                                       ((bundle.encoding == null || bundle.encoding.equals("")) ? "" : "_" + bundle.encoding) +
390                                        ".properties");
391            FileWriter fw = new FileWriter(outputFile);
392            bundle.writeContents(fw);
393            fw.flush();
394            fw.close();
395        }
396        // In case this is a newly created bundle or the location has changed recently, update the recent files, preference
397        Preferences.addRecentFilePreference(baseClass + ".properties", currentDirectory.getAbsolutePath() + File.separator +
398                                            baseClass + ".properties");
399    }
400
401    /**
402     * Calling this method removes a resource from the resource bundle. This method does not permanently
403     * erase the file containing the resources at this encoding, however any changes or saves that take
404     * place once this file has been removed will not be reflected in this hidden file. To restore the resource,
405     * the bundle will have to be recreated. (This last point may change)
406     */
407
408    public void hideResource(String encoding) {
409        for (int i=0; i < bundles.size(); i++) {
410            Bundle bundle = (Bundle)bundles.elementAt(i);
411            if (bundle.encoding.equals(encoding)) {
412                bundles.removeElement(bundle);
413                break;
414            }
415        }
416    }
417
418    /**
419     * Erases permanently one of the resource files. Be careful about calling this method there is nothing you can do
420     * once a file is erased.
421     */
422
423    public void eraseFile(String encoding) throws IOException {
424        for (int i = 0; i < bundles.size(); i++) {
425            Bundle bundle = (Bundle)bundles.elementAt(i);
426            if (!(bundle.encoding.equals(encoding))) continue;
427            File outputFile = new File(currentDirectory, baseClass +
428                                       ((bundle.encoding == null || bundle.encoding.equals("")) ? "" : "_" + bundle.encoding) +
429                                        ".properties");
430            boolean success = outputFile.delete();
431            if (!success) throw new IOException(Resources.getTranslation("error_deletion_not_possible"));
432            hideResource(encoding);
433            break;
434        }
435    }
436
437    /**
438     * Writes only one of the resource files to the file system. This file is specified by the encoding parameter
439     */
440
441    public void writeToFile(String encoding) throws IOException {
442        for (int i = 0; i < bundles.size(); i++) {
443            Bundle bundle = (Bundle)bundles.elementAt(i);
444            if (bundle.encoding.equals(encoding) || (i==0 && encoding.equals(""))) {
445                File outputFile = new File(currentDirectory, baseClass +
446                                           ((bundle.encoding == null || bundle.encoding.equals("")) ? "" : "_" + bundle.encoding) +
447                                            ".properties");
448                FileWriter fw = new FileWriter(outputFile);
449                bundle.writeContents(fw);
450                fw.flush();
451                fw.close();
452                break;
453            }
454        }
455        // In case this is a newly created bundle or the location has changed recently, update the recent files, preference
456        Preferences.addRecentFilePreference(baseClass + ".properties", currentDirectory.getAbsolutePath() + File.separator +
457                                            baseClass + ".properties");
458    }
459
460    /**
461     * Given a BundleItem and some properties to change for that item, this method first checks to make sure the passed
462     * item is valid and if it is, the properties of that item are changed to reflect those passed in as parameters to this
463     * method.
464     * @return true if the BundleItem was valid and updateable, false if otherwise (in this case no changes were made).
465     */
466
467    public boolean editItem(BundleItem item, String name, String value, String groupName, String comment, Hashtable lookups) {
468        if (name == null || name.equals("") || groupName == null || groupName.equals("") || item == null) return false;
469        String oldName = item.getKey();
470        String oldComment = item.getComment();
471        String oldValue = item.getTranslation();
472        //String oldGroupName = item.getParentGroup().getName();
473        // Loop through the bundles
474        for (int i = 0; i < bundles.size(); i++) {
475            Bundle bundle = (Bundle)bundles.elementAt(i);
476            BundleItem oldItem = (BundleItem)bundle.allItems.get(oldName);
477            if (oldItem == null) break;
478            if (!oldName.equals(name)) {
479                // A new key
480                oldItem.setKey(name);
481                bundle.allItems.remove(oldItem);
482                bundle.allItems.put(oldItem.getKey(), oldItem);
483            }
484            if (oldItem.getComment() == null || oldItem.getComment().equals(oldComment)) oldItem.setComment(comment);
485            if (oldItem.getTranslation().equals(oldValue)) oldItem.setTranslation(value);
486            oldItem.setLookups(lookups);
487            if (!oldItem.getParentGroup().getName().equals(groupName)) {
488                // A new group
489                oldItem.getParentGroup().removeBundleItem(oldItem.getKey());
490                BundleGroup bg = bundle.getBundleGroup(groupName);
491                if (bg == null) bg = bundle.getUngroupedGroup();
492                oldItem.setParentGroup(bg);
493                bg.addBundleItem(oldItem);
494            }
495        }
496        return true;
497    }
498
499    /**
500     * Attempts to create a new item in each of the language files. The method first checks the base Resource Bundle
501     * to make sure that the item name does not all ready exist. If it does exist the item is not created.
502     * @param name The unique key of the item
503     * @param value The translation of the item for the base class
504     * @param groupName The group name, should all ready exist in the base class
505     * @param comment An optional comment to be added to the item, can be <CODE>null</CODE>
506     * @return An error response. If the creation was successful <CODE>true</CODE> is returned, if there was an error <CODE>false</CODE> is returned.
507     */
508
509    public boolean createItem(String name, String value, String groupName, String comment, Hashtable lookups) {
510        if (name == null || name.equals("") || groupName == null || groupName.equals("")) return false;
511        Bundle mainBundle = (Bundle)bundles.firstElement();
512        BundleGroup mainGroup = null;
513        if (mainBundle.allItems.containsKey(name)) return false;
514        for (int i=0; i < mainBundle.getGroupCount(); i++) {
515            BundleGroup bg = mainBundle.getBundleGroup(i);
516            if (bg.getName().equals(groupName)) {mainGroup = bg; break;}
517        }
518        if (mainGroup == null) return false;
519        // Add to the base class
520        BundleItem mainItem = new BundleItem(mainGroup, name, value);
521        mainItem.setTranslated(true);
522        mainItem.setCreator(currentUser);
523        mainItem.setModifier(currentUser);
524        mainItem.setComment(comment);
525        mainBundle.allItems.put(name, mainItem);
526        mainGroup.addBundleItem(mainItem);
527        if (lookups != null) mainItem.setLookups(lookups);
528        // Add to the rest of the bundles
529        for (int i=1; i < bundles.size(); i++) {
530            Bundle bundle = (Bundle)bundles.elementAt(i);
531            // Find the group
532            BundleGroup group = null;
533            for (int j=0; j < bundle.getGroupCount(); j++) {
534                BundleGroup bg = bundle.getBundleGroup(j);
535                if (bg.getName().equals(groupName)) {group = bg; break;}
536            }
537            if (group == null) {
538                group = new BundleGroup(bundle, groupName);
539                bundle.addBundleGroup(group);
540            }
541            BundleItem item = new BundleItem(group, name, value);
542            item.setCreator(currentUser);
543            item.setModifier(currentUser);
544            item.setComment(comment);
545            if (lookups != null) item.setLookups(lookups);
546            bundle.allItems.put(name, item);
547            bundle.addUntranslatedItem(item);
548            group.addBundleItem(item);
549        }
550        return true;
551    }
552
553    /**
554     * Attempts to create a new group in each of the language files. The method first checks the base Resource Bundle
555     * to make sure that the group name does not all ready exist. If it does exist the group is not created.
556     * @param groupName The unique group name to be created
557     * @param groupComment An optional comment to be added to the group, can be <CODE>null</CODE>
558     * @return An error response. If the creation was successful <CODE>true</CODE> is returned, if there was an error <CODE>false</CODE> is returned.
559     */
560    public boolean createGroup(String groupName, String groupComment) {
561        if (groupName == null || groupName.equals(""))
562        	return false;
563        // Check to see if the group exists
564        Bundle mainBundle = (Bundle)bundles.firstElement();
565        if (mainBundle.hasGroup(groupName))
566        	return false;
567
568        // Create the group
569        for (int i=0; i < bundles.size(); i++) {
570            Bundle bundle = (Bundle)bundles.elementAt(i);
571            BundleGroup bg = new BundleGroup(bundle, groupName);
572            if (groupComment != null)
573            	bg.setComment(groupComment);
574            bundle.addBundleGroup(bg);
575        }
576        return true;
577    }
578
579    /**
580     * Removes a group and all of the items within that group from the various
581     * Resource Bundles known to the system. This method removes the group from
582     * the protected vector of groups, then removes all items in that group from
583     * the protected vector of untranslated items, and the protected hashtable of
584     * all items.
585     */
586
587    public void deleteGroup(String groupName) {
588        if (groupName == null) return;
589        // Loop through all of the bundles;
590        for (int i=0; i < bundles.size(); i++) {
591            Bundle bundle = (Bundle)bundles.elementAt(i);
592            bundle.removeGroup(groupName);
593        }
594    }
595
596    /**
597     * Remove resource items of the given name from each of the resource bundles that the system
598     * knows about. This works by first removing the item from the protected vector of translated
599     * items, if it is there, and then removing it from the the hashtable of all items, and then
600     * removing it from its respective group.
601     */
602
603    public void deleteItem(String itemName) {
604        if (itemName == null) return;
605        // Loop through all of the bundles;
606        for (int i=0; i < bundles.size(); i++) {
607            // Loop through untranslated items
608            Bundle bundle = (Bundle)bundles.elementAt(i);
609            bundle.removeUntranslatedItem(itemName);
610
611            // Loop through all Items
612            Enumeration items = bundle.allItems.elements();
613            while(items.hasMoreElements()) {
614                BundleItem item = (BundleItem)items.nextElement();
615                if (item.getKey().equals(itemName)) {
616                    bundle.allItems.remove(item);
617                    item.getParentGroup().removeBundleItem(item.getKey());
618                }
619            }
620        }
621    }
622
623    /**
624     * Looks through the resources contained in the bundle for a resource of the given encoding. Note that this
625     * search is case sensitive.
626     * @return True if the encoding exists as one of the resource files, false otherwise
627     */
628
629    public boolean hasResource(String encoding) {
630        // Check to see if the encoding exists
631        for (int i=0; i < bundles.size(); i++) {
632            Bundle b = (Bundle)bundles.elementAt(i);
633            if (b.encoding.equals(encoding)) return true;
634        }
635        return false;
636    }
637
638    /**
639     * Attempts to create a new resource file with the given encoding. The method first checks the base Resource Bundle
640     * to make sure that encoding does not all ready exist. If it does exist the resource file is not created.
641     * @param title An optional, quick title for the file, can be <CODE>null</CODE>
642     * @param comment An optional comment to be added to the resource, can be <CODE>null</CODE>
643     * @param manager The name of the person responsible for this resource, can be <CODE>null</CODE>
644     * @param encoding The proper encoding for the resource. Must be of form 'language', 'language_country', or 'language_country_variant'
645     * @param language A more formal name for the language (e.g. 'English', 'Deutsch', etc.), can be <CODE>null</CODE>
646     * @param country A more formal name for the country described by the resource, can be <CODE>null</CODE>
647     * @param variant A more formal name for the variant described by the resource, can be <CODE>null</CODE>
648     * @param copyValues An indication of wether or not to populate the resource with the items in the base class
649     * @return An error response. If the creation was successful <CODE>true</CODE> is returned, if there was an error <CODE>false</CODE> is returned.
650     */
651
652    public boolean createResource(String title, String comment, String manager, String encoding,
653                                  String language, String country, String variant, boolean copyValues) {
654        if (encoding == null || encoding.equals("") || encoding.startsWith("_")) return false;
655        // Check to see if the encoding exists
656        if (hasResource(encoding)) return false;
657        // Create the resource
658        Bundle bundle = new Bundle(encoding);
659        bundle.name = title;
660        bundle.comment = comment;
661        bundle.manager = manager;
662        bundle.language = language;
663        bundle.country = country;
664        bundle.variant = variant;
665
666        // Create a default group
667        bundle.addBundleGroup("Ungrouped Items", "These are resource items that have not been assigned a group");
668
669        if (copyValues) {
670            Bundle mainBundle = (Bundle)bundles.firstElement();
671            for (int i=0; i < mainBundle.getGroupCount(); i++) {
672                BundleGroup mainGroup = mainBundle.getBundleGroup(i);
673                BundleGroup bg = new BundleGroup(bundle,mainGroup.getName());
674                bg.setComment(mainGroup.getComment());
675                bundle.addBundleGroup(bg);
676                for (int j=0; j < mainGroup.getItemCount(); j++) {
677                    BundleItem mainItem = mainGroup.getBundleItem(j);
678                    BundleItem item = new BundleItem(bg, mainItem.getKey(), mainItem.getTranslation());
679                    item.setComment(mainItem.getComment());
680                    item.setCreator(mainItem.getCreator());
681                    item.setModifier(mainItem.getModifier());
682                    item.setLookups(new Hashtable());
683                    // TODO: This should be done in the Bundle class
684                    Enumeration keys = mainItem.getLookups().keys();
685                    while (keys.hasMoreElements()) {
686                        String name = (String)keys.nextElement();
687                        String value = (String)mainItem.getLookups().get(name);
688                        item.getLookups().put(new String(name), new String(value));
689                    }
690                    bg.addBundleItem(item);
691                    bundle.addUntranslatedItem(item);
692                }
693            }
694        }
695
696        bundles.addElement(bundle);
697
698        return true;
699    }
700
701    /**
702     * Returns the number of duplicate NLS entries
703     */
704
705    public int getNumberDuplicates() {
706        return ((Bundle)bundles.firstElement()).duplicates.size();
707    }
708
709    /**
710     * Returns a single string with a comma delimited listing of all duplicate entries found in the NLS resources
711     */
712
713    public String getDuplicatesListing() {
714        return listStrings(getDuplicatesListingVector());
715    }
716
717    /**
718     * Returns a Vector collection of duplicate BundleItems found in the bundle
719     */
720
721    public Vector getDuplicatesListingVector() {
722        return ((Bundle)bundles.firstElement()).duplicates;
723    }
724
725    /**
726     * A useful debugging method that lists the various BundleGroup names in a String.
727     */
728
729    public String getGroupListing() {
730        return listStrings(getGroupListingVector());
731    }
732
733    /**
734     * Returns a vector collection of all of the BundleGroup items founds int the bundle.
735     */
736
737    public Vector getGroupListingVector() {
738        Vector v = new Vector();
739        Bundle bundle = (Bundle)bundles.firstElement();
740        for (int i=0; i < bundle.getGroupCount(); i++) {
741            String name = bundle.getBundleGroup(i).getName();
742            v.addElement(name);
743        }
744        return v;
745    }
746
747    /**
748     * Returns the total number of languages that the system seems to support
749     */
750
751    public int getNumberLanguages() {
752        return bundles.size();
753    }
754
755    /**
756     * Returns a single string comprised of a comma delimited listing of all languages the system seems to support
757     */
758
759    public String getLanguageListing() {
760        return listStrings(getLanguageListingVector());
761    }
762
763    /**
764     * Returns a vector of strings comprising a list of all languages in the system
765     */
766
767    public Vector getLanguageListingVector() {
768        Vector v = new Vector();
769
770        for (int i = 0; i < bundles.size(); i++) {
771            Bundle dict = (Bundle)bundles.elementAt(i);
772            String dictStr = new String();
773            if (dict.language != null) dictStr += dict.language;
774            if (dict.country != null) dictStr += " " + dict.country;
775            if (dict.variant != null) dictStr += " " + dict.variant;
776            if (dictStr.trim().equals("")) dictStr = (dict.encoding.trim().equals("") ? "Base Resource Bundle" : dict.encoding);
777            v.addElement(dictStr);
778        }
779
780        return v;
781    }
782
783    /**
784     * Returns the number of translations contained across all language files
785     */
786
787    public int getNumberTotalTranslations() {
788        return allBundleKeys.size();
789    }
790
791    /**
792     * Returns the number of BundleGroups in  the bundle.
793     */
794
795    public int getNumberGroups() {
796        return ((Bundle)bundles.firstElement()).getGroupCount();
797    }
798
799    /**
800     * Returns the name of the user currently using the editor
801     */
802
803    public String getUser() {
804        return currentUser;
805    }
806
807    /**
808     * Sets the name of the user currently using the editor
809     */
810
811    public void setUser(String user) {
812        currentUser = user;
813    }
814
815    /**
816     * Sets the name of the base class associated with this resource bundle
817     */
818
819    public void setBaseClass(String baseClassName) {
820        baseClass = baseClassName;
821    }
822
823    /**
824     * Sets the directory in the file system in which this resource bundle is to be
825     * saved and retrieved.
826     */
827
828    public void setFileDirectory(File directory) {
829        if (directory.isDirectory()) currentDirectory = directory;
830    }
831
832    /**
833     * Returns the base class name if known, or "Unknown Base Class" otherwise.
834     */
835    public String toSring() {
836        return (baseClass == null ? "Unknown Base Class" : baseClass);
837    }
838
839    /**
840     * Returns the base class name or null if it does not exist.
841     */
842
843    public String getBaseClass() {
844        return baseClass;
845    }
846
847    /**
848     * A Vector of NLSbundles, one for each language
849     */
850    public Vector getBundles() {
851        return bundles;
852    }
853
854    /**
855     * Return a bundle from a locale
856     * @return The requested resource bundle
857     */
858    public Bundle getBundle(String locale) {
859    	Bundle bundle = null;
860        if (hasResource(locale)) {
861            for (int i = 0; i < bundles.size(); i++) {
862                Bundle tempb = (Bundle)bundles.elementAt(i);
863                if (tempb.encoding.equals(locale)) {
864                    bundle = tempb;
865                    break;
866                }
867            }
868        }
869        return bundle;
870    }
871
872    /**
873     * Returns the name of the file that is the base class file for the resource bundle.
874     */
875
876    public File getBaseFile() {
877        return new File(currentDirectory,baseClass + ".properties");
878    }
879
880    // Return a single comma delimited string made from a vector of strings
881    private String listStrings(Vector v) {
882        String retStr = new String();
883        for (int i = 0; i < v.size(); i++) {
884            Object o = v.elementAt(i);
885            if (!(o instanceof String)) continue;
886            String s = (String)o;
887            if (i > 0) retStr += ", ";
888            retStr += s;
889        }
890        return retStr;
891    }
892
893    // Init - called before ant construction
894    private void init() {
895        allBundleKeys = new Vector();
896        bundles = new Vector();
897        currentUser = "Unknown";
898    }
899
900    // Return a hashtable of the tags in a comment line (i.e. the text after each '@' character) and their values
901    private Hashtable getDescriptors(Hashtable result, String line) {
902        // Recursion terminating condition
903        if (line == null || line.length() <= 0 || line.indexOf("@") < 0) return result;
904        // Otherwise generate what information we can and recurse
905        if (result == null) result = new Hashtable();
906        // Strip off any information before and including a '@'
907        line = line.substring(line.indexOf("@")+1, line.length());
908        // There should be a space after the '@_tag_' and the value of this property
909        if (line.indexOf(" ") < 0) return result;        // This shouldn't happen if things are formatted right
910        // Add the text after the '@' character up to the first whitespace (has to be a space, not tab or other whitespace)
911        String name = line.substring(0,line.indexOf(" ")).trim();
912        // Now strip off the tag name
913        line = line.substring(line.indexOf(" "), line.length());
914        // If there is another '@' character we take the value up until that character
915        if (line.indexOf("@") >= 0) {
916            result.put(name,line.substring(0,line.indexOf("@")).trim());
917        }
918        // Otherwise we take the rest of the characters in the line
919        else {
920            result.put(name,line.trim());
921            return result;
922        }
923        // Recurse
924        return getDescriptors(result, line.substring(line.indexOf("@"), line.length()));
925    }
926
927    // Checks an array of strings to see if it contains a particular string
928/*    private static boolean arrayContains(String[] array, String match) {
929        for (int i = 0; i < array.length; i++) {
930            if (array[i].equals(match)) return true;
931        }
932        return false;
933    }*/
934
935    // Prints the usage of the program when called from main
936/*    private static void printUsage() {
937        String usage = new String();
938        usage += "Usage:\n\njava com.ibm.almaden.TempusFugit.Tools.RBManager fileName ((-r | -d) encoding?)?";
939        usage += "\n\n  fileName -> The file (and path?) representing the main NLS resource\n\t\t(i.e. TempusFugit.resources)\n";
940        usage += "  encoding -> Returns results for only the language encoding specified\n";
941        usage += "  flag -r  -> Gives only a status report on the state of the translations\n";
942        System.out.println(usage);
943    }*/
944
945}
946
947