src/share/classes/sun/tools/jconsole/Resources.java

Print this page
rev 5340 : 7017818: NLS: JConsoleResources.java cannot be handled by translation team
Reviewed-by: duke

@@ -23,91 +23,193 @@
  * questions.
  */
 
 package sun.tools.jconsole;
 
+import java.awt.event.KeyEvent;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 import java.text.MessageFormat;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 
-import sun.tools.jconsole.resources.JConsoleResources;
-
 /**
- * Provides resource support for jconsole.
+ * Toolkit that provides resource support for JConsole.
  */
 public final class Resources {
+    private static Map<String, Integer> MNEMONIC_LOOKUP = Collections
+            .synchronizedMap(new HashMap<String, Integer>());
 
-    private static final Object lock = new Object();
-    private static JConsoleResources resources = null;
-    static {
-        try {
-            resources =
-                (JConsoleResources)ResourceBundle.getBundle("sun.tools.jconsole.resources.JConsoleResources");
-        } catch (MissingResourceException e) {
-            // gracefully handle this later
+    private Resources() {
+        throw new AssertionError();
         }
+
+    /**
+     * Convenience method for {@link MessageFormat#format(String, Object...)}.
+     *
+     * @param pattern
+     *            the pattern
+     * @param objects
+     *            the arguments for the pattern
+     *
+     * @return a formatted string
+     */
+    public static String format(String pattern, Object... arguments) {
+            return MessageFormat.format(pattern, arguments);
     }
 
-    private Resources() { throw new AssertionError(); }
+    /**
+     * Returns the mnemonic for a message.
+     *
+     * @param message
+     *
+     * @return the mnemonic <code>int</code>
+     */
+    public static int getMnemonicInt(String message) {
+        Integer integer = MNEMONIC_LOOKUP.get(message);
+        if (integer != null) {
+            return integer.intValue();
+        }
+        return 0;
+    }
 
     /**
-     * Returns the text of the jconsole resource for the specified key
-     * formatted with the specified arguments.
+     * Initializes all non-final public static fields in the given class with
+     * messages from a {@link ResourceBundle}.
      *
+     * @param clazz
+     *            the class containing the fields
      */
-    public static String getText(String key, Object... args) {
-        String format = getString(key);
-        if (format == null) {
-            format = "missing resource key: key = \"" + key + "\", " +
-                "arguments = \"{0}\", \"{1}\", \"{2}\"";
+    public static void initializeMessages(Class<?> clazz, String rbName) {
+        ResourceBundle rb = null;
+        try {
+            rb = ResourceBundle.getBundle(rbName);
+        } catch (MissingResourceException mre) {
+            // fall through, handled later
+        }
+        for (Field field : clazz.getFields()) {
+            if (isWritableField(field)) {
+                String key = field.getName();
+                String message = getMessage(rb, key);
+                int mnemonicInt = findMnemonicInt(message);
+                message = removeMnemonicAmpersand(message);
+                message = replaceWithPlatformLineFeed(message);
+                setFieldValue(field, message);
+                MNEMONIC_LOOKUP.put(message, mnemonicInt);
+            }
+        }
         }
-        return formatMessage(format, args);
+
+    private static boolean isWritableField(Field field) {
+        int modifiers = field.getModifiers();
+        return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)
+                && !Modifier.isFinal(modifiers);
     }
 
-    static String formatMessage(String format, Object... args) {
-        String ss = null;
-        synchronized (lock) {
-            /*
-             * External synchronization required for safe use of
-             * java.text.MessageFormat:
+    /**
+     * Returns the message corresponding to the key in the bundle or a text
+     * describing it's missing.
+     *
+     * @param rb
+     *            the resource bundle
+     * @param key
+     *            the key
+     * @return the message
+     */
+    private static String getMessage(ResourceBundle rb, String key) {
+        if (rb == null) {
+            return "missing resource bundle";
+        }
+        try {
+            return rb.getString(key);
+        } catch (MissingResourceException mre) {
+            return "missing message for key = \"" + key
+                    + "\" in resource bundle ";
+        }
+    }
+
+    private static void setFieldValue(Field field, String value) {
+        try {
+            field.set(null, value);
+        } catch (IllegalArgumentException | IllegalAccessException e) {
+            // ignore
+        }
+    }
+
+    /**
+     * Returns a {@link String} where all <code>\n</code> in the <text> have
+     * been replaced with the line separator for the platform.
+     *
+     * @param text
+     *
+     * @return the replaced text
              */
-            ss = MessageFormat.format(format, args);
+    private static String replaceWithPlatformLineFeed(String text) {
+        return text.replace("\n", System.getProperty("line.separator"));
         }
-        return ss;
+
+    /**
+     * Removes the mnemonic identifier (<code>&</code>) from a string unless
+     * it's escaped by <code>&&</code> or placed at the end.
+     *
+     * @param message
+     *            the message
+     *
+     * @return a message with the mnemonic identifier removed
+     */
+    private static String removeMnemonicAmpersand(String message) {
+        StringBuilder s = new StringBuilder();
+        for (int i = 0; i < message.length(); i++) {
+            char current = message.charAt(i);
+            if (current != '&' || i == message.length() - 1
+                    || message.charAt(i + 1) == '&') {
+                s.append(current);
+            }
+        }
+        return s.toString();
     }
 
     /**
-     * Returns the mnemonic keycode int of the jconsole resource for the specified key.
+     * Finds the mnemonic character in a message.
+     *
+     * The mnemonic character is the first character followed by the first
+     * <code>&</code> that is not followed by another <code>&</code>.
      *
+     * @return the mnemonic as an <code>int</code>, or <code>0</code> if it
+     *         can't be found.
      */
-    public static int getMnemonicInt(String key) {
-        int mnemonic = 0;
-        if (resources != null) {
-            Object obj = resources.getObject(key+".mnemonic");
-            if (obj instanceof Character) {
-                mnemonic = (int)(Character)obj;
-                if (mnemonic >= 'a' && mnemonic <='z') {
-                    mnemonic -= ('a' - 'A');
+    private static int findMnemonicInt(String s) {
+        for (int i = 0; i < s.length() - 1; i++) {
+            if (s.charAt(i) == '&') {
+                if (s.charAt(i + 1) != '&') {
+                    return lookupMnemonicInt(s.substring(i + 1, i + 2));
+                } else {
+                    i++;
                 }
-            } else if (obj instanceof Integer) {
-                mnemonic = (Integer)obj;
             }
         }
-        return mnemonic;
+        return 0;
     }
 
     /**
-     * Returns the jconsole resource string for the specified key.
+     * Lookups the mnemonic for a key in the {@link KeyEvent} class.
      *
+     * @param c
+     *            the character to lookup
+     *
+     * @return the mnemonic as an <code>int</code>, or <code>0</code> if it
+     *         can't be found.
      */
-    private static String getString(String key) {
-        if (resources != null) {
+    private static int lookupMnemonicInt(String c) {
             try {
-                return resources.getString(key);
-            } catch (MissingResourceException e) {
-                return null;
-            }
+            return KeyEvent.class.getDeclaredField("VK_" + c.toUpperCase())
+                    .getInt(null);
+        } catch (IllegalArgumentException | IllegalAccessException
+                | NoSuchFieldException | SecurityException e) {
+            // Missing VK is okay
+            return 0;
         }
-        return "missing resource bundle: key = \"" + key + "\", " +
-            "arguments = \"{0}\", \"{1}\", \"{2}\"";
     }
 }