/* * File : Messages.java * Created : 14-sep-2001 11:22 * By : fbusquets * * JClic - Authoring and playing system for educational activities * * Copyright (C) 2000 - 2018 Francesc Busquets & Departament * d'Educacio de la Generalitat de Catalunya * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details (see the LICENSE file). */ package edu.xtec.util; import java.awt.BorderLayout; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.io.File; import java.text.Collator; import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.StringTokenizer; import java.util.TreeMap; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextField; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; /** * @author Francesc Busquets (fbusquets@xtec.cat) * @version 13.10.29 */ public class Messages { public static final String LANGUAGE = "language", COUNTRY = "country", VARIANT = "variant"; public static final String BASIC_BUNDLE = "messages.BasicMessages"; public static final String MESSAGES = "messages"; public static final String ERROR = "ERROR", WARNING = "WARNING"; public static final int OK = 0; // o public static final int YES = 1; // y public static final int RETRY = 2; // r public static final int NO = 3; // n public static final int IGNORE = 4; // i public static final int CANCEL = 5; // c public static final int YES_TO_ALL = 6; // Y public static final int NO_TO_ALL = 7; // N private static final int NUM_BUTTONS = 8; private static final String BTN_KEYS = "oyrnicYN"; private static final String[] BTN_CODES = { "OK", "YES", "RETRY", "NO", "IGNORE", "CANCEL", "YES_TO_ALL", "NO_TO_ALL" }; private String[] dlgButtons; public static final int MAX_PASSWORD_LENGTH = 24; private Locale currentLocale; private MultiBundle messages; private Collator collator; private java.text.NumberFormat numberFormat; private java.text.NumberFormat percentFormat; public static final String OPTIONS_DELIMITER = ","; public static final String[] KNOWN_LANGS = { "anglès", "arabic", "araucanian", "basque", "català", "catalán", "chinese", "english", "espanyol", "español", "esperanto", "euskara", "francés", "francès", "french", "gallego", "german", "greek", "inglés", "italian", "latin", "occità", "occitan", "portuguès", "romanian", "spanish", "swedish", "vasco" }; public static final String[] KNOWN_LANG_CODES = { "en", "ar", "arn", "eu", "ca", "ca", "ch", "en", "es", "es", "eo", "eu", "fr", "fr", "fr", "gl", "de", "gr", "en", "it", "la", "oc", "oc", "pt", "ro", "es", "sv", "eu" }; public Messages(String bundle) { init(bundle, null, null, null); } public Messages(String bundle, String options) { StringTokenizer st = new StringTokenizer(options, OPTIONS_DELIMITER); init(bundle, st.hasMoreTokens() ? st.nextToken() : null, st.hasMoreTokens() ? st.nextToken() : null, st.hasMoreTokens() ? st.nextToken() : null); } public Messages(String bundle, java.util.HashMap options) { init(bundle, (String) options.get(LANGUAGE), (String) options.get(COUNTRY), (String) options.get(VARIANT)); } public Messages(String bundle, String language, String country, String variant) { init(bundle, language, country, variant); } public static Messages getMessages(HashMap options, String bundle) { Messages msg = (Messages) options.get(MESSAGES); if (msg == null) { String language = (String) options.get(LANGUAGE); if (language == null) { JOptionPane pane = new JOptionPane("Please select your language:", JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION); pane.setSelectionValues(getDescriptiveLanguageCodes(null)); pane.setWantsInput(true); String initialSelection = getDescriptiveLanguageCode(Locale.getDefault().getLanguage()); pane.setInitialSelectionValue(initialSelection); showDlg((Component) options.get(Options.MAIN_PARENT_COMPONENT), pane, "Language selecion"); String sel = (String) pane.getInputValue(); if (sel == null) { sel = initialSelection; } options.put(LANGUAGE, getLanguageFromDescriptive(sel)); } msg = new Messages(bundle, options); options.put(MESSAGES, msg); Locale.setDefault(msg.getLocale()); } else if (bundle != null) { msg.setLocale(options); msg.addBundle(bundle); } return msg; } private void init(String bundle, String language, String country, String variant) { setLocale(language, country, variant); addBundle(bundle); addBundle(BASIC_BUNDLE); getDlgButtons(true); } public void setLocale(java.util.HashMap options) { setLocale((String) options.get(LANGUAGE), (String) options.get(COUNTRY), (String) options.get(VARIANT)); } public void setLocale(String language, String country, String variant) { Locale l; if (country == null) { country = ""; } if (language == null || language.length() == 0) { l = (currentLocale == null ? Locale.getDefault() : currentLocale); } else if (variant == null || variant.length() == 0) { l = new Locale(language, country); } else { l = new Locale(language, country, variant); } if (!l.equals(currentLocale)) { currentLocale = l; numberFormat = java.text.NumberFormat.getInstance(currentLocale); percentFormat = java.text.NumberFormat.getPercentInstance(currentLocale); collator = null; if (messages != null) { messages.setLocale(currentLocale); getDlgButtons(true); } } } public String[] getDlgButtons(boolean update) { if (update || dlgButtons == null) { dlgButtons = new String[NUM_BUTTONS]; for (int i = 0; i < NUM_BUTTONS; i++) { dlgButtons[i] = get(BTN_CODES[i]); } } return dlgButtons; } public void addBundle(String bundle) { if (currentLocale != null && bundle != null) { try { java.util.ResourceBundle b = ResourceManager.getBundle(bundle, currentLocale); if (messages == null) { messages = new MultiBundle(b, bundle, currentLocale); } else { messages.addBundle(b, bundle, currentLocale); } } catch (Exception ex) { System.err.println("unable to build messagesBundle: " + bundle); System.err.println(ex); } } } public String getShortDateStr(Date date) { DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, currentLocale); return df.format(date); } public String getShortDateTimeStr(Date date) { DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, currentLocale); return df.format(date); } public Date parseShortDate(String str) { Date result = null; if (str != null) { try { result = DateFormat.getDateInstance(DateFormat.SHORT, currentLocale).parse(str); } catch (java.text.ParseException ex) { // bad data! } } return result; } public String get(String group, String key) { return get(new StringBuilder(group).append(key).substring(0)); } public static String get(HashMap options, String group, String key) { return getMessages(options, null).get(group, key); } public String get(String key) { return messages == null ? key : messages.getString(key); } public static String get(HashMap options, String key) { return getMessages(options, null).get(key); } public String getAllowNull(String key) { return key == null ? "" : get(key); } public String get(String key1, String s, String key2) { StringBuilder sb = new StringBuilder(getAllowNull(key1)); sb.append(" ").append(s != null ? s : "").append(" ").append(getAllowNull(key2)); return sb.substring(0); } public Locale getLocale() { return currentLocale; } public Collator getCollator() { if (collator == null) { collator = Collator.getInstance(currentLocale); } return collator; } protected String[] parseButtons(String btnCodes) { btnCodes = StrUtils.secureString(btnCodes, "o"); String[] result = new String[btnCodes.length()]; String[] dlg = getDlgButtons(false); for (int i = 0; i < btnCodes.length(); i++) { int k = Math.max(BTN_KEYS.indexOf(btnCodes.charAt(i)), 0); result[i] = dlg[k]; } return result; } protected JButton[] parseJButtons(String btnCodes) { btnCodes = StrUtils.secureString(btnCodes, "o"); JButton[] result = new JButton[btnCodes.length()]; String[] dlg = getDlgButtons(false); for (int i = 0; i < btnCodes.length(); i++) { int k = Math.max(BTN_KEYS.indexOf(btnCodes.charAt(i)), 0); result[i] = new JButton(dlg[k]); result[i].setActionCommand(BTN_CODES[k]); } return result; } public int showQuestionDlg(Component parent, String key, String titleKey, String buttons) { return showQuestionDlgObj(parent, get(key), titleKey, buttons); } public int showQuestionDlgObj(Component parent, Object msg, String titleKey, String buttons) { NarrowOptionPane pane = new NarrowOptionPane(60, msg, JOptionPane.QUESTION_MESSAGE, JOptionPane.DEFAULT_OPTION, null, parseButtons(buttons)); String title = get(StrUtils.secureString(titleKey, "QUESTION")); return getFeedback(parent, pane, title); } public String showInputDlg(Component parent, String msgKey, String shortPromptKey, String initialValue, String titleKey, boolean isPassword) { String[] msgKeys = null; if (msgKey != null) { msgKeys = new String[] { msgKey }; } return showInputDlg(parent, msgKeys, shortPromptKey, initialValue, titleKey, isPassword); } public String showInputDlg(Component parent, String[] msgKeys, String shortPromptKey, String initialValue, String titleKey, boolean isPassword) { String result = null; JTextField textField; if (isPassword) { textField = new JPasswordField(MAX_PASSWORD_LENGTH); if (shortPromptKey == null) { shortPromptKey = "PASSWORD"; } } else { textField = new JTextField(MAX_PASSWORD_LENGTH); } if (initialValue != null) { textField.setText(initialValue); } if (showInputDlg(parent, msgKeys, new String[] { shortPromptKey }, new JComponent[] { textField }, titleKey)) { if (isPassword) { char[] pwch = ((JPasswordField) textField).getPassword(); if (pwch != null && pwch.length > 0) { result = String.copyValueOf(pwch); } } else { result = textField.getText(); } } return result; } public boolean showInputDlg(Component parent, String[] msgKeys, String[] shortPromptKeys, JComponent[] promptObjects, String titleKey) { ArrayList v = new ArrayList(); if (msgKeys != null) { for (String msgKey : msgKeys) { v.add(get(msgKey)); } } if (promptObjects != null) { if (shortPromptKeys == null) { for (JComponent jc : promptObjects) { v.add(jc); } } else { GridBagLayout gridBag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.insets = new java.awt.Insets(3, 3, 3, 3); JPanel panel = new JPanel(gridBag); for (int i = 0; i < promptObjects.length; i++) { if (shortPromptKeys.length > i) { JLabel lb = new JLabel(get(shortPromptKeys[i])); lb.setLabelFor(promptObjects[i]); lb.setHorizontalAlignment(SwingConstants.LEFT); c.gridwidth = GridBagConstraints.RELATIVE; gridBag.setConstraints(lb, c); panel.add(lb); } c.gridwidth = GridBagConstraints.REMAINDER; gridBag.setConstraints(promptObjects[i], c); panel.add(promptObjects[i]); } v.add(panel); } } String title = (titleKey != null ? get(titleKey) : ""); NarrowOptionPane pane = new NarrowOptionPane(60, v.toArray(), JOptionPane.QUESTION_MESSAGE, JOptionPane.DEFAULT_OPTION, null, parseButtons("oc")); return getFeedback(parent, pane, title) == OK; } public boolean showInputDlg(Component parent, JComponent mainComponent, String titleKey) { return showInputDlg(parent, mainComponent, titleKey, "oc"); } public boolean showInputDlg(Component parent, JComponent mainComponent, String titleKey, String buttons) { return showInputDlg(parent, mainComponent, titleKey, buttons, false); } public boolean showInputDlg(Component parent, JComponent mainComponent, String titleKey, String buttons, boolean centerOnParent) { InputDlg dlg = new InputDlg(parent, titleKey, buttons, mainComponent, centerOnParent); return dlg.getFeedback() == OK; } class InputDlg extends JDialog implements java.awt.event.ActionListener { int result = CANCEL; Component parent; boolean centerOnParent; InputDlg(Component parent, String titleKey, String buttons, JComponent mainComponent, boolean centerOnParent) { // 26-jan-06 - Modified to solve bug #73, reported by Jorda Polo // Compile error in gcj 4.0.3: // "Can't reference 'this' before the superclass constructor has been called." // OLD CODE: // super(JOptionPane.getFrameForComponent(parent), titleKey!=null ? // get(titleKey) : "", true); // NEW CODE: // Split in two steps: // 1 - call super with 'owner' and 'modal' parameters // 2 - if 'titleKey' not null, set title super(JOptionPane.getFrameForComponent(parent), true); if (titleKey != null) { setTitle(get(titleKey)); } // -------- this.parent = parent; this.centerOnParent = centerOnParent; setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); getContentPane().setLayout(new BorderLayout(10, 10)); if (mainComponent != null) { getContentPane().add(mainComponent, BorderLayout.CENTER); } JButton[] btn = parseJButtons(buttons); JPanel btnPanel = new JPanel(); JButton defaultBtn = null; for (JButton b : btn) { btnPanel.add(b); b.addActionListener(this); String cmd = b.getActionCommand(); if (defaultBtn == null && (BTN_CODES[OK].equals(cmd) || BTN_CODES[YES].equals(cmd))) { defaultBtn = b; } } getContentPane().add(btnPanel, BorderLayout.SOUTH); if (defaultBtn != null) { getRootPane().setDefaultButton(defaultBtn); } } public void actionPerformed(java.awt.event.ActionEvent ev) { if (ev != null) { String cmd = ev.getActionCommand(); for (int i = 0; i < BTN_CODES.length; i++) { if (BTN_CODES[i].equals(cmd)) { result = i; break; } } setVisible(false); } } public int getFeedback() { pack(); Component cmp = centerOnParent ? parent : JOptionPane.getFrameForComponent(parent); if (cmp != null) { int pw = cmp.getWidth(); int ph = cmp.getHeight(); setLocation((pw - getWidth()) / 2, (ph - getHeight()) / 2); setLocationRelativeTo(cmp); } if (!showDlg(this)) { result = CANCEL; } return result; } } public void showAlert(Component parent, String key) { showAlert(parent, new String[] { get(key) }); } public void showAlert(Component parent, String[] msg) { System.err.println("Warning:"); for (String s : msg) { System.err.println(s); } NarrowOptionPane pane = new NarrowOptionPane(60, msg, JOptionPane.WARNING_MESSAGE, JOptionPane.DEFAULT_OPTION, null, parseButtons(null)); getFeedback(parent, pane, get(WARNING)); } public int showErrorWarning(Component parent, String key, Exception ex) { return showErrorWarning(parent, key, (List) null, ex, null); } public int showErrorWarning(Component parent, String key, Exception ex, String buttons) { return showErrorWarning(parent, key, (List) null, ex, buttons); } public int showErrorWarning(Component parent, String key, String value, Exception ex, String buttons) { List v = new ArrayList(); if (value != null) { v.add(value); } return showErrorWarning(parent, key, v, ex, buttons); } public int showErrorWarning(Component parent, String key, List values, Exception ex, String buttons) { if (key == null) { key = ERROR; } List v = new ArrayList(); String mainMsg = get(key); System.err.println(mainMsg); v.add(mainMsg); if (values != null) { Iterator it = values.iterator(); while (it.hasNext()) { Object o = it.next(); if (o != null) { v.add(o); System.err.println(o); } } } if (ex != null) { String s = ex.getLocalizedMessage(); if (s != null) { v.add(s); } else { v.add(ex.toString()); } System.err.println(s); ex.printStackTrace(System.err); } NarrowOptionPane pane = new NarrowOptionPane(60, v.toArray(), JOptionPane.ERROR_MESSAGE, JOptionPane.DEFAULT_OPTION, null, parseButtons(buttons)); return getFeedback(parent, pane, get(ERROR)); } public boolean confirmReadableFile(Component parent, File f) { boolean result = f.canRead(); if (!result) { showAlert(parent, new String[] { get("FILE_BEG"), quote(f.getAbsolutePath()), get(f.exists() ? "FILE_NOT READABLE" : "FILE_NOT_EXIST") }); } return result; } public int confirmOverwriteFile(Component parent, File f, String buttons) { int result = YES; if (f.exists()) { boolean dir = f.isDirectory(); List v = new ArrayList(); v.add(get(dir ? "FILE_DIR_BEG" : "FILE_BEG")); v.add(quote(f.getAbsolutePath())); boolean readOnly = !f.canWrite(); if (readOnly) { v.add(get("FILE_READONLY")); } else { v.add(get("FILE_EXISTS")); v.add(get(dir ? "FILE_OVERWRITE_DIR_PROMPT" : "FILE_OVERWRITE_PROMPT")); } if (readOnly) { showErrorWarning(parent, ERROR, v, null, null); result = CANCEL; } else { NarrowOptionPane pane = new NarrowOptionPane(60, v.toArray(), JOptionPane.QUESTION_MESSAGE, JOptionPane.DEFAULT_OPTION, null, parseButtons(buttons)); result = getFeedback(parent, pane, get("CONFIRM")); } } return result; } private static boolean showDlg(Component parent, JOptionPane pane, String title) { if (parent != null && !(parent instanceof java.awt.Frame)) { parent = JOptionPane.getFrameForComponent(parent); } JDialog dialog = pane.createDialog(parent, title); pane.selectInitialValue(); return showDlg(dialog); } public static boolean showDlg(final JDialog dialog) { if (SwingUtilities.isEventDispatchThread()) { dialog.setVisible(true); } else { try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { dialog.setVisible(true); } }); } catch (Exception ex) { System.err.println("Show dialog error: " + ex); return false; } } return true; } protected int getFeedback(Component parent, JOptionPane pane, String title) { int result = CANCEL; if (showDlg(parent, pane, title)) { Object selectedValue = pane.getValue(); if (selectedValue != null) { for (int i = 0; i < NUM_BUTTONS; i++) { if (dlgButtons[i].equals(selectedValue)) { result = i; break; } } } } return result; } public static String quote(String text) { return new StringBuilder(" \"").append(text).append("\" ").substring(0); } public String kValue(long v) { StringBuilder sb = new StringBuilder(20); sb.append(numberFormat.format(v / 1024)).append(" Kb"); return sb.substring(0); } public String fileSize(long size) { StringBuilder sb = new StringBuilder(20); int kb = (int) size / 1024; double mb = ((double) size) / (1024 * 1024); if (kb == 0) { sb.append(numberFormat.format(size)).append(" bytes"); } else if (mb < 1.0) { sb.append(numberFormat.format(kb)).append(" Kb"); } else { int digits = numberFormat.getMaximumFractionDigits(); numberFormat.setMaximumFractionDigits(1); sb.append(numberFormat.format(mb)).append(" Mb"); numberFormat.setMaximumFractionDigits(digits); } return sb.substring(0); } public String getNumber(long v) { return numberFormat.format(v); } public String getNumber(double v) { return numberFormat.format(v); } public String getPercent(long v) { return percentFormat.format(((double) v) / 100); } public String getPercent(double v) { return percentFormat.format(v); } public String getHmsTime(long milis) { long v = milis / 1000; if (v < 1) { v = 1; } StringBuilder sb = new StringBuilder(50); if (v >= 3600) { sb.append(v / 3600).append("h"); } if (v >= 60) { sb.append((v % 3600) / 60).append("'"); } sb.append(v % 60).append("\""); return sb.substring(0); } /* * Since the Java specification does not include the ISO 639-2 three-letter * language codes, we provide this HashMap to support it. The list will be * expanded as new translations of JClic where created. */ public static final HashMap ISO_639_2_CODES = new HashMap(); static { ISO_639_2_CODES.put("ast", "asturianu"); ISO_639_2_CODES.put("vec", "vèneto"); } public static String getDescriptiveLanguageCode(String languageCode) { String result = null; if (languageCode != null) { result = (String) ISO_639_2_CODES.get(languageCode); if (result == null) { Locale lx = new Locale(languageCode, ""); result = lx.getDisplayName(Locale.getDefault()); } result = result + " (" + languageCode + ")"; } return result; } public static String getLanguageFromDescriptive(String descriptive) { String result = null; int p = -1; if (descriptive != null && (descriptive = descriptive.trim()).length() > 4 && (p = descriptive.lastIndexOf('(')) > 0) { result = descriptive.substring(p + 1, descriptive.length() - 1); } return result; } private static HashMap descriptiveLanguageCodes = new HashMap(); public static String[] getDescriptiveLanguageCodes(Locale inLocale) { String key = (inLocale == null ? "null" : inLocale.toString()); String[] result = descriptiveLanguageCodes.get(key); if (result == null) { Locale dl = Locale.getDefault(); String[] lc = Locale.getISOLanguages(); TreeMap tree = new TreeMap(); for (String l : lc) { String s; // EXCEPTIONS: if (inLocale == null && l.equals("eu")) { s = "euskara"; } else if (inLocale == null && l.equals("gl")) { s = "galego"; } else { Locale lx = new Locale(l, ""); s = lx.getDisplayName(inLocale == null ? lx : dl); } tree.put(l, s + " (" + l + ")"); } for (String k : ISO_639_2_CODES.keySet()) { String s = (String) ISO_639_2_CODES.get(k); tree.put(k, s + " (" + k + ")"); } result = new String[tree.size()]; Iterator it = tree.values().iterator(); int i = 0; while (it.hasNext()) { result[i++] = (String) it.next(); } descriptiveLanguageCodes.put(key, result); } return result; } public String[] getDescriptiveLanguageCodes() { return getDescriptiveLanguageCodes(getLocale()); } public static String getKnownLanguageCode(String language) { String result = null, lang = (language == null) ? "" : language.toLowerCase(); for (int i = 0; i < KNOWN_LANGS.length; i++) { if (KNOWN_LANGS[i].equals(lang)) { result = KNOWN_LANG_CODES[i]; break; } } return result; } private static HashMap codesToNames; private static HashMap namesToCodes; private static void buildLanguageMaps() { String[] dlc = getDescriptiveLanguageCodes(null); codesToNames = new HashMap(dlc.length); namesToCodes = new HashMap(dlc.length); for (String c : dlc) { // Allow country codes with more than two letters int p = c.lastIndexOf('(') - 1; String name = c.substring(0, p).toLowerCase(); String code = c.substring(p + 2, c.length() - 1); codesToNames.put(code, name); namesToCodes.put(name, code); } } public static HashMap getCodesToNames() { if (codesToNames == null) { buildLanguageMaps(); } return codesToNames; } public static HashMap getNamesToCodes() { if (namesToCodes == null) { buildLanguageMaps(); } return namesToCodes; } }