PageRenderTime 168ms CodeModel.GetById 60ms app.highlight 75ms RepoModel.GetById 26ms app.codeStats 0ms

/src/mpv5/i18n/LanguageManager.java

http://mp-rechnungs-und-kundenverwaltung.googlecode.com/
Java | 597 lines | 454 code | 44 blank | 99 comment | 78 complexity | 5f2be267a6569bda3d1619ae64751efc MD5 | raw file
  1/*
  2 *  This file is part of YaBS.
  3 *
  4 *  YaBS is free software: you can redistribute it and/or modify
  5 *  it under the terms of the GNU General Public License as published by
  6 *  the Free Software Foundation, either version 3 of the License, or
  7 *  (at your option) any later version.
  8 *
  9 *  YaBS is distributed in the hope that it will be useful,
 10 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 11 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12 *  GNU General Public License for more details.
 13 *
 14 *  You should have received a copy of the GNU General Public License
 15 *  along with YaBS.  If not, see <http://www.gnu.org/licenses/>.
 16 */
 17package mpv5.i18n;
 18
 19import java.io.File;
 20import java.io.FileNotFoundException;
 21import java.net.URI;
 22import java.util.ArrayList;
 23import java.util.Collections;
 24import java.util.Enumeration;
 25import java.util.Hashtable;
 26import java.util.List;
 27import java.util.Locale;
 28
 29import java.util.ResourceBundle;
 30import java.util.Vector;
 31import javax.swing.ComboBoxModel;
 32import javax.swing.DefaultComboBoxModel;
 33import javax.swing.SwingUtilities;
 34import mpv5.Main;
 35import mpv5.YabsViewProxy;
 36import mpv5.db.common.*;
 37import mpv5.db.objects.User;
 38import mpv5.globals.LocalSettings;
 39import mpv5.globals.Messages;
 40import mpv5.logging.Log;
 41import mpv5.ui.dialogs.Notificator;
 42import mpv5.ui.dialogs.Popup;
 43import mpv5.ui.frames.MPView;
 44
 45import mpv5.utils.arrays.ArrayUtilities;
 46import mpv5.utils.date.DateConverter;
 47import mpv5.utils.files.FileDirectoryHandler;
 48import mpv5.utils.files.FileReaderWriter;
 49import mpv5.utils.models.MPComboBoxModelItem;
 50import mpv5.utils.models.MPComboboxModel;
 51import mpv5.utils.reflection.ClasspathTools;
 52import mpv5.utils.text.RandomText;
 53import mpv5.utils.xml.XMLReader;
 54import org.jdom.Document;
 55
 56/**
 57 *
 58 *  Administrator
 59 */
 60public class LanguageManager {
 61
 62    static {
 63    }
 64    protected final static String defLanguageBundleName = "mpv5/resources/languages/Panels";
 65    protected static ResourceBundle defLanguageBundle = ResourceBundle.getBundle(defLanguageBundleName);
 66    private static String[][] defLanguage = new String[][]{{"buildin_en", "English"}};
 67    private static Hashtable<String, ResourceBundle> cachedLanguages = new Hashtable<String, ResourceBundle>();
 68    private static boolean cached = false;
 69
 70    /**
 71     * Flushes the Language Cache so that it contains no Languages.
 72     */
 73    public static void flushLanguageCache() {
 74        cachedLanguages.clear();
 75    }
 76
 77    /**
 78     * Get a country name by id
 79     * @param id
 80     * @return
 81     */
 82    public static String getCountryName(int id) {
 83        for (int i = 0; i < COUNTRIES.size(); i++) {
 84            MPComboBoxModelItem mPComboBoxModelItem = COUNTRIES.get(i);
 85            if (mPComboBoxModelItem.getId().equals(String.valueOf(id))) {
 86                return mPComboBoxModelItem.getValue();
 87            }
 88        }
 89        return "---";
 90    }
 91
 92    /**
 93     * Import & replace the country list
 94     * @param file
 95     */
 96    public static void importCountries(File file) {
 97        XMLReader r = new XMLReader();
 98        try {
 99            Document doc = r.newDoc(file, false);
100            //            if (MPSecurityManager.checkAdminAccess() && !mpv5.db.objects.User.getCurrentUser().isDefault()) {
101
102            if (doc != null) {
103                ArrayList<DatabaseObject> users = User.getObjects(Context.getUser(), true);
104                try {
105
106                    for (int i = 0; i < users.size(); i++) {
107                        User u = (User) users.get(i);
108                        QueryHandler.instanceOf().clone(Context.getCountries()).delete(new String[][]{{"groupsids", String.valueOf(u.__getGroupsids()), ""}});
109                    }
110                } catch (Exception ex) {
111                    Log.Debug(ex);
112                }
113
114                String[][] countries1 = r.toArray(r.getSubRootElement("countries"));
115
116                for (int k = 0; k < users.size(); k++) {
117                    DatabaseObject databaseObject = users.get(k);
118                    for (int i = 0; i < countries1.length; i++) {
119                        try {
120                            String[] country = countries1[i];
121                            QueryData t = new QueryData();
122                            t.add("cname", country[1]);
123                            t.add("iso", Integer.valueOf(country[2]));
124                            t.add("groupsids", databaseObject.__getGroupsids());
125                            QueryHandler.instanceOf().clone(Context.getCountries()).insert(t, Messages.DONE.toString());
126                        } catch (Exception exception) {
127                            Log.Debug(LanguageManager.class, exception.getMessage());
128                        }
129                    }
130                }
131
132//                mpv5.YabsViewProxy.instance().addMessage(langname + Messages.ROW_UPDATED);
133            }
134//            } else {
135//                Popup.notice(Messages.ADMIN_ACCESS + "\n" + Messages.DEFAULT_USER);
136//            }
137        } catch (Exception x) {
138            Log.Debug(LanguageManager.class, x);
139        }
140    }
141
142    private static boolean isCachedLanguage(String langid) {
143        return cachedLanguages.containsKey(langid);
144    }
145
146    private static void cachelanguage(String langid, ResourceBundle bundle) {
147        Log.Debug(LanguageManager.class, "Caching language with id " + langid);
148        if (cachedLanguages.containsKey(langid)) {
149            cachedLanguages.remove(langid);
150        }
151        cachedLanguages.put(langid, bundle);
152        cached = true;
153    }
154
155    private static ResourceBundle getCachedLanguage(String langid) {
156        return cachedLanguages.get(langid);
157    }
158
159    /**
160     * Checks the existance of a language in DB
161     * @param languagename
162     * @return
163     */
164    public static boolean checkLanguage(String languagename) {
165        Object[][] data = QueryHandler.instanceOf().clone(Context.getLanguage()).select(Context.SEARCH_NAME, new String[]{Context.SEARCH_NAME, languagename, "'"});
166        if (data != null && data.length > 0) {
167            return true;
168        } else {
169            return false;
170        }
171    }
172    private static volatile boolean failed = false;
173    private static final List<String> cacheRunWatchdog = Collections.synchronizedList(new ArrayList<String>());
174
175    /**
176     * 
177     * @param langid
178     * @return
179     */
180    public static ResourceBundle getBundle(String langid) {
181        synchronized (LanguageManager.class) {
182            if (!langid.contentEquals("buildin_en")) {
183//                Log.Debug(LanguageManager.class, "Checking language: " + langid);
184                if (!failed) {
185//                    Log.Debug(LanguageManager.class, "Language has not previously failed: " + langid);
186                    // if not cached AND not tried to cache it before
187                    if (!isCachedLanguage(langid) && !cacheRunWatchdog.contains(langid)) {
188                        //cache it
189                        Log.Debug(LanguageManager.class, "Language is not cached: " + langid);
190                        cacheRunWatchdog.add(langid);
191
192                        File bundlefile = null;
193                        Object[] data;
194                        URI newfileUri;
195                        String tempname = langid;
196                        try {
197                            data = QueryHandler.instanceOf().clone(Context.getLanguage()).
198                                    selectFirst("filename", new String[]{Context.SEARCH_NAME, langid, "'"});
199                            if (data != null && data.length > 0) {
200                                bundlefile = QueryHandler.instanceOf().clone(Context.getFiles()).retrieveFile(String.valueOf(
201                                        data[0]));
202                            } else {
203                                fail(langid);
204                                failed = true;
205                                return ResourceBundle.getBundle(defLanguageBundleName);
206                            }
207                            if (bundlefile != null) {
208                                newfileUri = FileDirectoryHandler.copyFile(bundlefile, new File(LocalSettings.getProperty(LocalSettings.CACHE_DIR)), tempname + ".properties", false, true);
209                                ClasspathTools.addPath(new File(LocalSettings.getProperty(LocalSettings.CACHE_DIR)));
210                                File newbundle = new File(newfileUri);
211                                if (hasNeededKeys(newbundle, false) || addNeededKeys(newbundle)) {
212                                    Log.Debug(LanguageManager.class, "File has needed keys for language: " + langid);
213                                    Log.Debug(LanguageManager.class, "Created language file at: " + newfileUri);
214                                    try {
215                                        ResourceBundle bundle = ResourceBundleUtf8.getBundle(tempname);
216                                        cachelanguage(langid, bundle);
217                                        return bundle;
218                                    } catch (Exception e) {
219                                        fail(langid);
220                                        failed = true;
221                                        Log.Debug(LanguageManager.class, e);
222                                        return defLanguageBundle;
223                                    }
224                                } else {
225                                    fail(langid);
226                                    failed = true;
227                                    return defLanguageBundle;
228                                }
229                            } else {
230                                fail(langid);
231                                failed = true;
232                                return defLanguageBundle;
233                            }
234                        } catch (NodataFoundException nd) {
235                            //language has been deleted?
236                            Log.Debug(LanguageManager.class, nd.getMessage());
237                            return ResourceBundle.getBundle(defLanguageBundleName);
238                        } catch (Exception e) {
239                            failed = true;
240                            fail(langid);
241                            Log.Debug(LanguageManager.class, e);
242                            return defLanguageBundle;
243                        }
244
245                    } else if (isCachedLanguage(langid)) {
246                        return getCachedLanguage(langid);
247                    } else if (cacheRunWatchdog.contains(langid)) {
248                        failed = true;
249                        Log.Debug(LanguageManager.class, "Already tried to cache language with id: " + langid);
250                        fail(langid);
251                        return defLanguageBundle;
252                    } else {
253                        return defLanguageBundle;
254                    }
255                } else {
256                    return defLanguageBundle;
257                }
258            }
259        }
260
261        return defLanguageBundle;
262    }
263
264    /**
265     * The users selected language bundle, or default if not defined by the user
266     * @return
267     */
268    public static ResourceBundle getBundle() {
269        if (Main.INSTANTIATED) {
270            return getBundle(mpv5.db.objects.User.getCurrentUser().__getLanguage());
271        } else {
272            return ResourceBundle.getBundle(defLanguageBundleName);
273        }
274    }
275
276    /**
277     *
278     * @param langid
279     * @return
280     */
281    public static Object[][] getEditorModel(String langid) {
282        ResourceBundle bundle = getBundle(langid);
283        Enumeration<String> set = bundle.getKeys();
284        ArrayList<String[]> list = new ArrayList<String[]>();
285
286        while (set.hasMoreElements()) {
287            String string = set.nextElement();
288            list.add(new String[]{string, bundle.getString(string), null});
289        }
290
291        return ArrayUtilities.listToStringArrayArray(list);
292    }
293
294    /**
295     *
296     * @return A ComboBoxModel reflecting the available Languages
297     */
298    public static ComboBoxModel getLanguagesAsComboBoxModel() {
299        Object[][] data = QueryHandler.instanceOf().clone(Context.getLanguage()).select("cname, longname", (String[]) null);
300        MPComboBoxModelItem[] t = null;
301        Object[][] ldata;
302        ldata = ArrayUtilities.merge(defLanguage, data);
303        t = MPComboBoxModelItem.toItems(ldata);
304        return new DefaultComboBoxModel(t);
305    }
306    public static List<MPComboBoxModelItem> COUNTRIES;
307
308    /**
309     *
310     * @return A ComboBoxModel reflecting the available Countries
311     */
312    public static synchronized mpv5.utils.models.MPComboboxModel getCountriesAsComboBoxModel() {
313        if (COUNTRIES == null) {
314            COUNTRIES = new Vector<MPComboBoxModelItem>();
315            try {
316                QueryCriteria2 q = new QueryCriteria2();
317                q.and(new QueryParameter(Context.getCountries(), "groupsids", User.getCurrentUser().__getGroupsids(), QueryParameter.EQUALS));
318                Object[][] data = QueryHandler.instanceOf().clone(Context.getCountries()).getColumns(new String[]{"iso", "cname"}, 0, q);
319                for (int i = 0; i < data.length; i++) {
320                    Object[] objects = data[i];
321                    COUNTRIES.add(new MPComboBoxModelItem(objects[0].toString(), objects[1].toString()));
322                }
323                Log.Debug(LanguageManager.class, "Cached countries: " + COUNTRIES.size());
324                return MPComboBoxModelItem.toModel(COUNTRIES);
325            } catch (NodataFoundException ex) {
326                Log.Debug(LanguageManager.class, ex.getMessage());
327                return new MPComboboxModel(new MPComboBoxModelItem[]{});
328            }
329        } else {
330            return MPComboBoxModelItem.toModel(COUNTRIES);
331        }
332    }
333
334    /**
335     *
336     * @return A comboBoxModel reflecting the available locales
337     */
338    public static DefaultComboBoxModel getLocalesAsComboBoxModel() {
339        Locale[] o = Locale.getAvailableLocales();
340        MPComboBoxModelItem[] items = new MPComboBoxModelItem[o.length];
341        for (int i = 0; i < items.length; i++) {
342            String language = o[i].getLanguage();
343            String country = o[i].getCountry();
344            String locale_name = o[i].getDisplayName();
345//            Log.Debug(LanguageManager.class, locale_name);
346            items[i] = new MPComboBoxModelItem(language + "_" + country,
347                    locale_name + "  [" + language + "_" + country + "]");
348//            items[i] = new MPComboBoxModelItem(o[i].toString(), o[i].getDisplayName());
349        }
350
351        return new DefaultComboBoxModel(ArrayUtilities.sort(items));
352    }
353
354    /**
355     * Imports a language file to DB
356     * @param langname
357     * @param file If it is a .zip, will get extracted and then processed
358     * @throws UnsupportedOperationException
359     */
360    public static String importLanguage(String langname, File file) throws Exception {
361        try {
362            String langid = new RandomText(10).getString();
363
364            file = FileDirectoryHandler.unzipFile(file);
365            Log.Debug(LanguageManager.class, "Importing: " + file);
366
367            if (hasNeededKeys(file, true)) {
368                try {
369                    Runnable runnable = new Runnable() {
370
371                        public void run() {
372                            mpv5.YabsViewProxy.instance().setWaiting(true);
373                        }
374                    };SwingUtilities.invokeLater(runnable);
375                    String dbname = QueryHandler.instanceOf().clone(Context.getFiles()).insertFile(file);
376                    try {
377                        Thread.sleep(3333);
378                    } catch (InterruptedException ex) {
379                        Log.Debug(ex);
380                    }
381                    QueryData t = new QueryData();
382                    t.add("cname", langid);
383                    t.add("longname", langname);
384                    t.add("filename", dbname);
385                    t.add("dateadded", DateConverter.getTodayDBDate());
386                    Log.Debug(LanguageManager.class, "Adding language: " + langname);
387                    int id = QueryHandler.instanceOf().clone(Context.getLanguage()).insert(t, "Imported language: " + langname);
388                    if (id > 0) {
389                        mpv5.YabsViewProxy.instance().addMessage(langname + Messages.INSERTED.toString());
390                        Popup.notice(langname + Messages.INSERTED.toString());
391                        cachelanguage(langid, getBundle(langid));
392                    } else {
393                        mpv5.YabsViewProxy.instance().addMessage(Messages.ERROR_OCCURED.toString());
394                        Popup.notice(Messages.ERROR_OCCURED.toString());
395                    }
396                } catch (FileNotFoundException ex) {
397                    Log.Debug(LanguageManager.class, ex);
398                } catch (Exception x) {
399                    //insert was not possible
400                    throw new UnsupportedOperationException("Language could not be inserted, file already exists!?");
401                }
402                return langid;
403            } else {
404                return null;
405            }
406        } catch (Exception exception) {
407            Popup.error(exception);
408            throw exception;
409        } finally {
410            mpv5.YabsViewProxy.instance().setWaiting(false);
411        }
412    }
413
414    /**
415     * Deletes a language from db
416     * @param langid
417     * @throws mpv5.db.common.NodataFoundException
418     */
419    public static void removeLanguage(String langid) throws NodataFoundException {
420        Object[] data;
421        if (!langid.contentEquals("buildin_en")) {
422            data = QueryHandler.instanceOf().clone(Context.getLanguage()).
423                    selectFirst("filename", new String[]{Context.SEARCH_NAME, langid, "'"});
424            if (data != null && data.length > 0) {
425                try {
426                    QueryHandler.instanceOf().clone(Context.getFiles()).removeFile(String.valueOf(data[0]));
427                } catch (Exception ex) {
428                    Log.Debug(ex);
429                }
430            }
431        } else {
432            Popup.notice(Messages.NOT_POSSIBLE);
433        }
434    }
435
436    private static boolean hasNeededKeys(File file, boolean popupOnError) {
437        synchronized (new LanguageManager()) {
438            try {
439                List<String> failures = new ArrayList<String>();
440                Enumeration<String> keys = ResourceBundle.getBundle(defLanguageBundleName).getKeys();
441                File impFile = file;
442                FileReaderWriter frw = new FileReaderWriter(impFile);
443                String[] lines = frw.readLines();
444
445                while (keys.hasMoreElements()) {
446                    String string = keys.nextElement();
447                    boolean found = false;
448                    for (int i = 0; i < lines.length; i++) {
449                        String line = lines[i];
450                        if (line.startsWith(string)) {
451                            found = true;
452                        }
453                    }
454                    if (!found) {
455                        failed = true;
456                        Log.Debug(LanguageManager.class, "Key '" + string + "' not found in file " + file);
457                        if (!popupOnError) {
458                            mpv5.YabsViewProxy.instance().addMessage(Messages.ERROR_OCCURED.toString());
459                            return false;
460                        } else {
461                            try {
462                                failures.add(string + "=" + ResourceBundle.getBundle(defLanguageBundleName).getString(string));
463                            } catch (Exception e) {
464                                failures.add(string + "=???");
465                            }
466                        }
467                    }
468                }
469                if (popupOnError && !failures.isEmpty()) {
470                    Popup.notice(failures, "Import not possible.\nMissing keys in " + file + ":\n");
471                }
472                return failures.isEmpty();
473            } catch (Exception e) {
474                Log.Debug(e);
475                return false;
476            }
477        }
478    }
479
480    /**
481     * Returns the status of the language manager
482     * @return true if at least one language is cached
483     */
484    public static boolean isReady() {
485        return cached;
486    }
487
488    private static boolean addNeededKeys(File file) {
489        Notificator.raiseNotification(Messages.LANGUAGE_FILE, true);
490        synchronized (new LanguageManager()) {
491            try {
492                Enumeration<String> keys = ResourceBundle.getBundle(defLanguageBundleName).getKeys();
493                File impFile = file;
494                FileReaderWriter frw = new FileReaderWriter(impFile, "UTF8");
495                String[] lines = frw.readLinesWCharset();
496
497                while (keys.hasMoreElements()) {
498                    String string = keys.nextElement();
499                    boolean found = false;
500                    for (int i = 0; i < lines.length; i++) {
501                        String line = lines[i];
502                        if (line.startsWith(string)) {
503                            found = true;
504                        }
505                    }
506                    if (!found) {
507                        Log.Debug(LanguageManager.class, "Key '" + string + "' added to file " + file);
508                        frw.write(string + "=" + ResourceBundle.getBundle(defLanguageBundleName).getString(string));
509                    }
510                }
511                failed = false;
512                return !failed;
513            } catch (Exception e) {
514                Log.Debug(e);
515                fail(e.getMessage());
516                failed = true;
517                return false;
518            }
519        }
520    }
521
522    private static void fail(String langid) {
523        Log.Debug(LanguageManager.class, "Failed language: " + langid);
524        Log.Debug(LanguageManager.class, "Error loading additional languages. Using default now.");
525        mpv5.YabsViewProxy.instance().addMessage(Messages.ERROR_OCCURED.toString());
526    }
527
528    private static boolean exists(String langid) {
529        Object[] data;
530        if (!langid.contentEquals("buildin_en")) {
531            try {
532                data = QueryHandler.instanceOf().clone(Context.getLanguage()).selectFirst("filename", new String[]{Context.SEARCH_NAME, langid, "'"});
533                if (data != null && data.length > 0) {
534                    return true;
535                } else {
536                    return false;
537                }
538            } catch (NodataFoundException ex) {
539                return false;
540            }
541        } else {
542            return true;
543        }
544    }
545
546    /**
547     * Disable all languages other than the build-in one
548     */
549    public static void disableLanguages() {
550        failed = true;
551    }
552
553    /**
554     * Checks the cache directory for cached languages and loads the last cached one
555     */
556    public static void preLoadCachedLanguage() {
557        File cache = FileDirectoryHandler.getTempDirAsFile();
558        ClasspathTools.addPath(cache);
559        Log.Debug(LanguageManager.class, "[preload-lang] Preloading languages from: " + cache);
560        File[] candidates = cache.listFiles();
561        List<File> languages = new ArrayList<File>();
562        for (int i = 0; i < candidates.length; i++) {
563            File file = candidates[i];
564            if (file.getName().endsWith(".properties")) {
565                languages.add(file);
566            }
567        }
568        Log.Debug(LanguageManager.class, "[preload-lang] Found potential language files: " + languages.size());
569        File lastfile = null;
570        for (int i = 0; i < languages.size(); i++) {
571            File file = languages.get(i);
572            if (lastfile == null) {
573                lastfile = file;
574            }
575            if (file.lastModified() > lastfile.lastModified()) {
576                lastfile = file;
577            } else {
578                //file.deleteOnExit();
579            }
580        }
581
582        if (lastfile != null && hasNeededKeys(lastfile, false)) {
583            Log.Debug(LanguageManager.class, "[preload-lang] File has needed keys: " + lastfile);
584            //Add the files parent to classpath to be found
585            Log.Debug(LanguageManager.class, "[preload-lang] Using language file at: " + lastfile);
586            try {
587                ResourceBundle bundle = ResourceBundleUtf8.getBundle(lastfile.getName().substring(0, lastfile.getName().lastIndexOf(".")));
588                if (lastfile.canRead()) {
589                    defLanguageBundle = bundle;
590                    cachelanguage(lastfile.getName().substring(0, lastfile.getName().lastIndexOf(".")), bundle);
591                }
592            } catch (Exception e) {
593                Log.Debug(LanguageManager.class, e.getMessage());
594            }
595        }
596    }
597}