PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 1ms

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