sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1411581 - in /sis/branches/JDK7/sis-utility/src: main/java/org/apache/sis/util/resources/ test/java/org/apache/sis/util/resources/
Date Tue, 20 Nov 2012 08:33:07 GMT
Author: desruisseaux
Date: Tue Nov 20 08:33:06 2012
New Revision: 1411581

URL: http://svn.apache.org/viewvc?rev=1411581&view=rev
Log:
Serialization of ResourceInternationalString should use the key name rather than numerical values,
for better cross-version compatibility since the numerical values may change at any time.

Added:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/KeyConstants.java
      - copied, changed from r1411285, sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java
Modified:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/ResourceInternationalString.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/resources/IndexedResourceBundleTest.java

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1411581&r1=1411580&r2=1411581&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java Tue Nov 20 08:33:06 2012
@@ -41,7 +41,15 @@ public final class Errors extends Indexe
      * @version 0.3
      * @module
      */
-    public static final class Keys {
+    public static final class Keys extends KeyConstants {
+        /**
+         * The unique instance of key constants handler.
+         */
+        static final Keys INSTANCE = new Keys();
+
+        /**
+         * For {@link #INSTANCE} creation only.
+         */
         private Keys() {
         }
 
@@ -267,12 +275,11 @@ public final class Errors extends Indexe
     }
 
     /**
-     * Returns the {@code Keys} class.
+     * Returns the handle for the {@code Keys} constants.
      */
     @Override
-    final Class<?> getKeysClass() throws ClassNotFoundException {
-        assert super.getKeysClass() == Keys.class;
-        return Keys.class;
+    final KeyConstants getKeyConstants() {
+        return Keys.INSTANCE;
     }
 
     /**
@@ -375,9 +382,10 @@ public final class Errors extends Indexe
     private static final class International extends ResourceInternationalString {
         private static final long serialVersionUID = -229348959712294902L;
 
-        International(int key)              {super(key);}
-        International(int key, Object args) {super(key, args);}
-        @Override IndexedResourceBundle getBundle(Locale locale) {
+        International(int key)                   {super(key);}
+        International(int key, Object args)      {super(key, args);}
+        @Override KeyConstants getKeyConstants() {return Keys.INSTANCE;}
+        @Override IndexedResourceBundle getBundle(final Locale locale) {
             return getResources(locale);
         }
     }

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java?rev=1411581&r1=1411580&r2=1411581&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java Tue Nov 20 08:33:06 2012
@@ -22,7 +22,6 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.text.MessageFormat;
-import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.Locale;
 import java.util.MissingResourceException;
@@ -30,8 +29,6 @@ import java.util.NoSuchElementException;
 import java.util.ResourceBundle;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
 import net.jcip.annotations.ThreadSafe;
 
 import org.opengis.util.InternationalString;
@@ -42,8 +39,6 @@ import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.logging.Logging;
 
-import static org.apache.sis.util.Arrays.resize;
-
 
 /**
  * {@link ResourceBundle} implementation accepting integers instead of strings for resource keys.
@@ -78,15 +73,6 @@ public class IndexedResourceBundle exten
     private final String filename;
 
     /**
-     * The key names. This is usually not needed, but may be created from the {@code Keys}
-     * inner class in some occasions.
-     *
-     * @see #getKeyNames()
-     * @see #getKeyName(int)
-     */
-    private transient String[] keys;
-
-    /**
      * The array of resources. Keys are an array index. For example, the value for key "14" is
      * {@code values[14]}. This array will be loaded only when first needed. We should not load
      * it at construction time, because some {@code ResourceBundle} objects will never ask for
@@ -172,54 +158,22 @@ public class IndexedResourceBundle exten
     }
 
     /**
-     * Returns the inner {@code Keys} class which declare the key constants.
+     * Returns a handler for the constants declared in the inner {@code Keys} class.
      * Subclasses defined in the {@code org.apache.sis.util.resources} package
      * override this method for efficiency. However the default implementation
      * should work for other cases (we don't want to expose too much internal API).
      *
-     * @return The inner {@code Keys} class.
-     * @throws ClassNotFoundException If the inner class has not been found.
+     * @return A handler for the constants declared in the inner {@code Keys} class.
      */
-    Class<?> getKeysClass() throws ClassNotFoundException {
+    KeyConstants getKeyConstants() {
+        Class<?> keysClass = KeyConstants.class;
         for (final Class<?> inner : getClass().getClasses()) {
             if ("Keys".equals(inner.getSimpleName())) {
-                return inner;
-            }
-        }
-        throw new ClassNotFoundException();
-    }
-
-    /**
-     * Returns the internal array of key names. <strong>Do not modify the returned array.</strong>
-     * This method should usually not be invoked, in order to avoid loading the inner Keys class.
-     * The keys names are used only in rare situation, like {@link #list(Writer)} or in log records.
-     */
-    private synchronized String[] getKeyNames() {
-        if (keys == null) {
-            String[] names;
-            int length = 0;
-            try {
-                final Field[] fields = getKeysClass().getFields();
-                names = new String[fields.length];
-                for (final Field field : fields) {
-                    if (Modifier.isStatic(field.getModifiers()) && field.getType() == Integer.TYPE) {
-                        final int index = (Integer) field.get(null);
-                        if (index >= length) {
-                            length = index + 1;
-                            if (length > names.length) {
-                                // Usually don't happen, except for incomplete bundles.
-                                names = Arrays.copyOf(names, length*2);
-                            }
-                        }
-                        names[index] = field.getName();
-                    }
-                }
-            } catch (ReflectiveOperationException e) {
-                names = CharSequences.EMPTY_ARRAY;
+                keysClass = inner;
+                break;
             }
-            keys = resize(names, length);
         }
-        return keys;
+        return new KeyConstants(keysClass);
     }
 
     /**
@@ -229,7 +183,7 @@ public class IndexedResourceBundle exten
      */
     @Override
     public final Enumeration<String> getKeys() {
-        return new KeyEnum(getKeyNames());
+        return new KeyEnum(getKeyConstants().getKeyNames());
     }
 
     /**
@@ -269,22 +223,6 @@ public class IndexedResourceBundle exten
     }
 
     /**
-     * Returns the name of the key at the given index. If there is no name at that given
-     * index, format the index as a decimal number. Those decimal numbers are parsed by
-     * our {@link #handleGetObject(String)} implementation.
-     */
-    private String getKeyNameAt(final int index) {
-        final String[] keys = getKeyNames();
-        if (index < keys.length) {
-            final String key = keys[index];
-            if (key != null) {
-                return key;
-            }
-        }
-        return String.valueOf(index);
-    }
-
-    /**
      * Lists resources to the specified stream. If a resource has more than one line, only
      * the first line will be written. This method is used mostly for debugging purposes.
      *
@@ -294,7 +232,7 @@ public class IndexedResourceBundle exten
     @Debug
     public final void list(final Appendable out) throws IOException {
         int keyLength = 0;
-        final String[] keys = getKeyNames();
+        final String[] keys = getKeyConstants().getKeyNames();
         for (final String key : keys) {
             if (key != null) {
                 keyLength = Math.max(keyLength, key.length());
@@ -423,7 +361,7 @@ public class IndexedResourceBundle exten
              * LogRecords, for easier debugging if the message has not been properly formatted.
              */
             try {
-                keyID = (Integer) getKeysClass().getField(key).get(null);
+                keyID = getKeyConstants().getKeyValue(key);
             } catch (ReflectiveOperationException e) {
                 e.addSuppressed(exception);
                 Logging.recoverableException(getClass(), "handleGetObject", e);
@@ -663,7 +601,7 @@ public class IndexedResourceBundle exten
      * @return The log record.
      */
     public final LogRecord getLogRecord(final Level level, final int key) {
-        final LogRecord record = new LogRecord(level, getKeyNameAt(key));
+        final LogRecord record = new LogRecord(level, getKeyConstants().getKeyName(key));
         record.setResourceBundleName(getClass().getName());
         record.setResourceBundle(this);
         return record;

Copied: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/KeyConstants.java (from r1411285, sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/KeyConstants.java?p2=sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/KeyConstants.java&p1=sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java&r1=1411285&r2=1411581&rev=1411581&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/KeyConstants.java Tue Nov 20 08:33:06 2012
@@ -16,66 +16,29 @@
  */
 package org.apache.sis.util.resources;
 
-import java.io.BufferedInputStream;
-import java.io.DataInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.MessageFormat;
 import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.Locale;
-import java.util.MissingResourceException;
-import java.util.NoSuchElementException;
-import java.util.ResourceBundle;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import net.jcip.annotations.ThreadSafe;
-
-import org.opengis.util.InternationalString;
-
-import org.apache.sis.util.Debug;
-import org.apache.sis.util.Classes;
-import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.CharSequences;
-import org.apache.sis.util.logging.Logging;
 
 import static org.apache.sis.util.Arrays.resize;
 
 
 /**
- * {@link ResourceBundle} implementation accepting integers instead of strings for resource keys.
- * Using integers allow implementations to avoid adding large string constants into their
- * {@code .class} files and runtime images. Developers still have meaningful labels in their
- * code (e.g. {@code DimensionMismatch}) through a set of constants defined in {@code Keys}
- * inner classes, with the side-effect of compile-time safety. Because integer constants are
- * inlined right into class files at compile time, the declarative classes is never loaded at
- * run time.
- *
- * <p>This class also provides facilities for string formatting using {@link MessageFormat}.</p>
+ * Base class of {@code Keys} inner classes declaring key constants.
+ * This base class provides methods for fetching a key numeric value
+ * from its name and conversely.
  *
- * @author  Martin Desruisseaux (IRD, Geomatys)
- * @since   0.3 (derived from geotk-1.2)
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3
  * @version 0.3
  * @module
  */
-@ThreadSafe
-public class IndexedResourceBundle extends ResourceBundle {
-    /**
-     * Maximum string length for text inserted into another text. This parameter is used by
-     * {@link #summarize}. Resource strings are never cut to this length. However, text replacing
-     * {@code "{0}"} in a string like {@code "Parameter name is {0}"} will be cut to this length.
-     */
-    private static final int MAX_STRING_LENGTH = 200;
-
+class KeyConstants {
     /**
-     * The resource name of the binary file containing resources. It may be a file name or an
-     * entry in a JAR file. The path must be relative to the package containing the subclass
-     * of {@code IndexedResourceBundle}.
+     * The class that defines key constants.
      */
-    private final String filename;
+    private final Class<?> keysClass;
 
     /**
      * The key names. This is usually not needed, but may be created from the {@code Keys}
@@ -87,119 +50,31 @@ public class IndexedResourceBundle exten
     private transient String[] keys;
 
     /**
-     * The array of resources. Keys are an array index. For example, the value for key "14" is
-     * {@code values[14]}. This array will be loaded only when first needed. We should not load
-     * it at construction time, because some {@code ResourceBundle} objects will never ask for
-     * values. This is particularly the case for ancestor classes of {@code Resources_fr_CA},
-     * {@code Resources_en}, {@code Resources_de}, etc., which will only be used if a key has
-     * not been found in the subclass.
-     *
-     * @see #ensureLoaded(String)
-     */
-    private String[] values;
-
-    /**
-     * The locale for formatting objects like number, date, etc. There are two possible Locales
-     * we could use: default locale or resource bundle locale. If the default locale uses the same
-     * language as this ResourceBundle's locale, then we will use the default locale. This allows
-     * dates and numbers to be formatted according to user conventions (e.g. French Canada) even
-     * if the ResourceBundle locale is different (e.g. standard French). However, if languages
-     * don't match, then we will use ResourceBundle locale for better coherence.
-     */
-    private transient Locale formatLocale;
-
-    /**
-     * The object to use for formatting messages. This object
-     * will be constructed only when first needed.
-     */
-    private transient MessageFormat format;
-
-    /**
-     * The key of the last resource requested. If the same resource is requested multiple times,
-     * knowing its key allows us to avoid invoking the costly {@link MessageFormat#applyPattern}
-     * method.
-     */
-    private transient int lastKey;
-
-    /**
-     * Constructs a new resource bundle loading data from the given UTF file.
-     *
-     * @param filename The file or the JAR entry containing resources. The path must be relative
-     *        to the package of the {@code IndexedResourceBundle} subclass being constructed.
-     */
-    protected IndexedResourceBundle(final String filename) {
-        this.filename = filename;
-    }
-
-    /**
-     * Returns a resource bundle of the specified class.
-     *
-     * @param  <T>     The resource bundle class.
-     * @param  base    The resource bundle class.
-     * @param  locale  The locale, or {@code null} for the default locale.
-     * @return Resources in the given locale.
-     * @throws MissingResourceException if resources can't be found.
-     *
-     * @see Vocabulary#getResources(Locale)
-     * @see Errors#getResources(Locale)
+     * For sub-classes constructors only.
      */
-    protected static <T extends IndexedResourceBundle> T getBundle(Class<T> base, Locale locale)
-            throws MissingResourceException
-    {
-        if (locale == null) {
-            locale = Locale.getDefault();
-        }
-        // No caching; we rely on the one implemented in ResourceBundle.
-        return base.cast(getBundle(base.getName(), locale, base.getClassLoader(), Loader.INSTANCE));
-    }
-
-    /**
-     * Returns the locale to use for formatters. It is often the same than {@link #getLocale()},
-     * except if the later has the same language than the default locale, in which case this
-     * method returns the default locale. For example if this {@code IndexResourceBundle} is
-     * for the French locale but the user is French Canadian, we will format the dates using
-     * Canada French conventions rather than France conventions.
-     */
-    private Locale getFormatLocale() {
-        if (formatLocale == null) {
-            formatLocale = Locale.getDefault();
-            final Locale rl = getLocale(); // Sometime null with IBM's JDK.
-            if (rl != null && !formatLocale.getLanguage().equalsIgnoreCase(rl.getLanguage())) {
-                formatLocale = rl;
-            }
-        }
-        return formatLocale;
+    protected KeyConstants() {
+        keysClass = getClass();
     }
 
     /**
-     * Returns the inner {@code Keys} class which declare the key constants.
-     * Subclasses defined in the {@code org.apache.sis.util.resources} package
-     * override this method for efficiency. However the default implementation
-     * should work for other cases (we don't want to expose too much internal API).
-     *
-     * @return The inner {@code Keys} class.
-     * @throws ClassNotFoundException If the inner class has not been found.
+     * Creates a new instance for key constants defined in an independent class.
      */
-    Class<?> getKeysClass() throws ClassNotFoundException {
-        for (final Class<?> inner : getClass().getClasses()) {
-            if ("Keys".equals(inner.getSimpleName())) {
-                return inner;
-            }
-        }
-        throw new ClassNotFoundException();
+    KeyConstants(final Class<?> keysClass) {
+        this.keysClass = keysClass;
     }
 
     /**
      * Returns the internal array of key names. <strong>Do not modify the returned array.</strong>
      * This method should usually not be invoked, in order to avoid loading the inner Keys class.
-     * The keys names are used only in rare situation, like {@link #list(Writer)} or in log records.
+     * The keys names are used only in rare situation, like {@link IndexedResourceBundle#list(Writer)}
+     * or in log records.
      */
-    private synchronized String[] getKeyNames() {
+    final synchronized String[] getKeyNames() {
         if (keys == null) {
             String[] names;
             int length = 0;
             try {
-                final Field[] fields = getKeysClass().getFields();
+                final Field[] fields = keysClass.getFields();
                 names = new String[fields.length];
                 for (final Field field : fields) {
                     if (Modifier.isStatic(field.getModifiers()) && field.getType() == Integer.TYPE) {
@@ -223,57 +98,11 @@ public class IndexedResourceBundle exten
     }
 
     /**
-     * Returns an enumeration of the keys.
-     *
-     * @return All keys in this resource bundle.
-     */
-    @Override
-    public final Enumeration<String> getKeys() {
-        return new KeyEnum(getKeyNames());
-    }
-
-    /**
-     * The keys as an enumeration. This enumeration needs to skip null values, which
-     * may occur if the resource bundle is incomplete for that particular locale.
-     */
-    private static final class KeyEnum implements Enumeration<String> {
-        /** The keys to return.          */ private final String[] keys;
-        /** Index of next key to return. */ private int next;
-
-        /** Creates a new enum for the given array of keys. */
-        KeyEnum(final String[] keys) {
-            this.keys = keys;
-        }
-
-        /** Returns {@code true} if there is at least one more non-null key. */
-        @Override public boolean hasMoreElements() {
-            while (next < keys.length) {
-                if (keys[next] != null) {
-                    return true;
-                }
-                next++;
-            }
-            return false;
-        }
-
-        /** Returns the next key. */
-        @Override public String nextElement() {
-            while (next < keys.length) {
-                final String key = keys[next++];
-                if (key != null) {
-                    return key;
-                }
-            }
-            throw new NoSuchElementException();
-        }
-    }
-
-    /**
      * Returns the name of the key at the given index. If there is no name at that given
      * index, format the index as a decimal number. Those decimal numbers are parsed by
-     * our {@link #handleGetObject(String)} implementation.
+     * our {@link IndexedResourceBundle#handleGetObject(String)} implementation.
      */
-    private String getKeyNameAt(final int index) {
+    final String getKeyName(final int index) {
         final String[] keys = getKeyNames();
         if (index < keys.length) {
             final String key = keys[index];
@@ -285,469 +114,9 @@ public class IndexedResourceBundle exten
     }
 
     /**
-     * Lists resources to the specified stream. If a resource has more than one line, only
-     * the first line will be written. This method is used mostly for debugging purposes.
-     *
-     * @param  out The destination stream.
-     * @throws IOException if an output operation failed.
-     */
-    @Debug
-    public final void list(final Appendable out) throws IOException {
-        int keyLength = 0;
-        final String[] keys = getKeyNames();
-        for (final String key : keys) {
-            if (key != null) {
-                keyLength = Math.max(keyLength, key.length());
-            }
-        }
-        final String lineSeparator = System.lineSeparator();
-        final String[] values = ensureLoaded(null);
-        for (int i=0; i<values.length; i++) {
-            final String key   = keys  [i];
-            final String value = values[i];
-            if (key != null && value != null) {
-                int indexCR = value.indexOf('\r'); if (indexCR < 0) indexCR = value.length();
-                int indexLF = value.indexOf('\n'); if (indexLF < 0) indexLF = value.length();
-                final String number = String.valueOf(i);
-                out.append(CharSequences.spaces(5 - number.length()))
-                   .append(number)
-                   .append(": ")
-                   .append(key)
-                   .append(CharSequences.spaces(keyLength - key.length()))
-                   .append(" = ")
-                   .append(value, 0, Math.min(indexCR, indexLF))
-                   .append(lineSeparator);
-            }
-        }
-    }
-
-    /**
-     * Ensures that resource values are loaded. If they are not, loads them immediately.
-     *
-     * @param  key Key for the requested resource, or {@code null} if all resources
-     *         are requested. This key is used mostly for constructing messages.
-     * @return The resources.
-     * @throws MissingResourceException if this method failed to load resources.
-     */
-    private String[] ensureLoaded(final String key) throws MissingResourceException {
-        final String methodName = (key != null) ? "getObject" : "getKeys";
-        LogRecord record = null;
-        try {
-            String[] values;
-            synchronized (this) {
-                values = this.values;
-                if (values != null) {
-                    return values;
-                }
-                /*
-                 * Prepares a log record.  We will wait for successful loading before
-                 * posting this record.  If loading fails, the record will be changed
-                 * into an error record. Note that the message must be logged outside
-                 * the synchronized block, otherwise there is dead locks!
-                 */
-                record = new LogRecord(Level.FINER, "Loaded resources for {0} from bundle \"{1}\".");
-                /*
-                 * Loads resources from the UTF file.
-                 */
-                InputStream in;
-                String name = filename;
-                while ((in = getClass().getResourceAsStream(name)) == null) { // NOSONAR
-                    final int ext  = name.lastIndexOf('.');
-                    final int lang = name.lastIndexOf('_', ext-1);
-                    if (lang <= 0) {
-                        throw new FileNotFoundException(filename);
-                    }
-                    final int length = name.length();
-                    name = new StringBuilder(lang + (length-ext))
-                            .append(name, 0, lang).append(name, ext, length).toString();
-                }
-                try (DataInputStream input = new DataInputStream(new BufferedInputStream(in))) {
-                    this.values = values = new String[input.readInt()];
-                    for (int i=0; i<values.length; i++) {
-                        values[i] = input.readUTF();
-                        if (values[i].isEmpty()) {
-                            values[i] = null;
-                        }
-                    }
-                }
-                /*
-                 * Now, logs the message. This message is not localized.  Note that
-                 * Locale.getDisplayName() may return different string on different
-                 * Java implementation, but it doesn't matter here since we use the
-                 * result only for logging purpose.
-                 */
-                String language = null;
-                final Locale rl = getLocale(); // Sometime null with IBM's JDK.
-                if (rl != null) {
-                    language = rl.getDisplayName(Locale.US);
-                }
-                if (language == null || language.isEmpty()) {
-                    language = "<default>";
-                }
-                record.setParameters(new String[] {language, getClass().getCanonicalName()});
-            }
-            Logging.log(IndexedResourceBundle.class, methodName, record);
-            return values;
-        } catch (IOException exception) {
-            record.setLevel  (Level.WARNING);
-            record.setMessage(exception.getMessage()); // For administrator, use system locale.
-            record.setThrown (exception);
-            Logging.log(IndexedResourceBundle.class, methodName, record);
-            final MissingResourceException error = new MissingResourceException(
-                    Exceptions.getLocalizedMessage(exception, getLocale()), // For users, use requested locale.
-                    getClass().getCanonicalName(), key);
-            error.initCause(exception);
-            throw error;
-        }
-    }
-
-    /**
-     * Gets an object for the given key from this resource bundle.
-     * Returns null if this resource bundle does not contain an
-     * object for the given key.
-     *
-     * @param  key the key for the desired object
-     * @throws NullPointerException if {@code key} is {@code null}
-     * @return the object for the given key, or null
-     */
-    @Override
-    protected final Object handleGetObject(final String key) {
-        // Synchronization performed by 'ensureLoaded'
-        final String[] values = ensureLoaded(key);
-        int keyID;
-        try {
-            keyID = Integer.parseInt(key);
-        } catch (NumberFormatException exception) {
-            /*
-             * Maybe the full key name has been specified instead. We do that for localized
-             * LogRecords, for easier debugging if the message has not been properly formatted.
-             */
-            try {
-                keyID = (Integer) getKeysClass().getField(key).get(null);
-            } catch (ReflectiveOperationException e) {
-                e.addSuppressed(exception);
-                Logging.recoverableException(getClass(), "handleGetObject", e);
-                return null; // This is okay as of 'handleGetObject' contract.
-            }
-        }
-        return (keyID >= 0 && keyID < values.length) ? values[keyID] : null;
-    }
-
-    /**
-     * Returns {@code arguments} as an array, and convert some types that are not recognized
-     * by {@link MessageFormat}. If {@code arguments} is already an array, then that array or
-     * a copy of that array will be returned. If {@code arguments} is not an array, it will be
-     * placed in an array of length 1.
-     *
-     * <p>All the array elements will be checked for {@link CharSequence}, {@link InternationalString},
-     * {@link Throwable} or {@link Class} instances. All {@code InternationalString} instances will
-     * be localized according this resource bundle locale. Any characters sequences of length
-     * greater than {@link #MAX_STRING_LENGTH} will be reduced using the
-     * {@link CharSequences#shortSentence(CharSequence, int)} method.</p>
-     *
-     * @param  arguments The object to check.
-     * @return {@code arguments} as an array, eventually with some elements replaced.
-     */
-    private Object[] toArray(final Object arguments) {
-        Object[] array;
-        if (arguments instanceof Object[]) {
-            array = (Object[]) arguments;
-        } else {
-            array = new Object[] {arguments};
-        }
-        for (int i=0; i<array.length; i++) {
-            final Object element = array[i];
-            Object replacement = element;
-            if (element instanceof CharSequence) {
-                CharSequence text = (CharSequence) element;
-                if (text instanceof InternationalString) {
-                    text = ((InternationalString) element).toString(getFormatLocale());
-                }
-                replacement = CharSequences.shortSentence(text, MAX_STRING_LENGTH);
-            } else if (element instanceof Throwable) {
-                String message = Exceptions.getLocalizedMessage((Throwable) element, getFormatLocale());
-                if (message == null) {
-                    message = Classes.getShortClassName(element);
-                }
-                replacement = message;
-            } else if (element instanceof Class<?>) {
-                replacement = Classes.getShortName((Class<?>) element);
-            }
-            // No need to check for Numbers or Dates instances, since they are
-            // properly formatted in the ResourceBundle locale by MessageFormat.
-            if (replacement != element) {
-                if (array == arguments) {
-                    array = array.clone(); // Protect the user-provided array from change.
-                }
-                array[i] = replacement;
-            }
-        }
-        return array;
-    }
-
-    /**
-     * Gets a string for the given key and appends "…" to it.
-     * This method is typically used for creating menu items.
-     *
-     * @param  key The key for the desired string.
-     * @return The string for the given key.
-     * @throws MissingResourceException If no object for the given key can be found.
-     */
-    public final String getMenuLabel(final int key) throws MissingResourceException {
-        return getString(key) + '…';
-    }
-
-    /**
-     * Gets a string for the given key and appends ": " to it.
-     * This method is typically used for creating labels.
-     *
-     * @param  key The key for the desired string.
-     * @return The string for the given key.
-     * @throws MissingResourceException If no object for the given key can be found.
-     */
-    public final String getLabel(final int key) throws MissingResourceException {
-        return getString(key) + ": ";
-    }
-
-    /**
-     * Gets a string for the given key from this resource bundle or one of its parents.
-     *
-     * @param  key The key for the desired string.
-     * @return The string for the given key.
-     * @throws MissingResourceException If no object for the given key can be found.
-     */
-    public final String getString(final int key) throws MissingResourceException {
-        return getString(String.valueOf(key));
-    }
-
-    /**
-     * Gets a string for the given key and formats it with the specified argument. The message is
-     * formatted using {@link MessageFormat}. Calling this method is approximately equivalent to
-     * calling:
-     *
-     * {@preformat java
-     *     String pattern = getString(key);
-     *     Format f = new MessageFormat(pattern);
-     *     return f.format(arg0);
-     * }
-     *
-     * If {@code arg0} is not already an array, it will be placed into an array of length 1. Using
-     * {@link MessageFormat}, all occurrences of "{0}", "{1}", "{2}" in the resource string will be
-     * replaced by {@code arg0[0]}, {@code arg0[1]}, {@code arg0[2]}, etc.
-     *
-     * @param  key The key for the desired string.
-     * @param  arg0 A single object or an array of objects to be formatted and substituted.
-     * @return The string for the given key.
-     * @throws MissingResourceException If no object for the given key can be found.
-     *
-     * @see #getString(String)
-     * @see #getString(int,Object,Object)
-     * @see #getString(int,Object,Object,Object)
-     * @see MessageFormat
-     */
-    public final String getString(final int key, final Object arg0) throws MissingResourceException {
-        final String pattern = getString(key);
-        final Object[] arguments = toArray(arg0);
-        synchronized (this) {
-            if (format == null) {
-                /*
-                 * Constructs a new MessageFormat for formatting the arguments.
-                 */
-                format = new MessageFormat(pattern, getFormatLocale());
-                lastKey = key;
-            } else if (key != lastKey) {
-                /*
-                 * Method MessageFormat.applyPattern(...) is costly! We will avoid
-                 * calling it again if the format already has the right pattern.
-                 */
-                format.applyPattern(pattern);
-                lastKey = key;
-            }
-            try {
-                return format.format(arguments);
-            } catch (RuntimeException e) {
-                /*
-                 * Safety against badly implemented toString() method
-                 * in libraries that we don't control.
-                 */
-                return "[Unformattable message: " + e + ']';
-            }
-        }
-    }
-
-    /**
-     * Gets a string for the given key and replaces all occurrences of "{0}",
-     * "{1}", with values of {@code arg0}, {@code arg1}, etc.
-     *
-     * @param  key The key for the desired string.
-     * @param  arg0 Value to substitute for "{0}".
-     * @param  arg1 Value to substitute for "{1}".
-     * @return The formatted string for the given key.
-     * @throws MissingResourceException If no object for the given key can be found.
-     */
-    public final String getString(final int    key,
-                                  final Object arg0,
-                                  final Object arg1) throws MissingResourceException
-    {
-        return getString(key, new Object[] {arg0, arg1});
-    }
-
-    /**
-     * Gets a string for the given key and replaces all occurrences of "{0}",
-     * "{1}", with values of {@code arg0}, {@code arg1}, etc.
-     *
-     * @param  key The key for the desired string.
-     * @param  arg0 Value to substitute for "{0}".
-     * @param  arg1 Value to substitute for "{1}".
-     * @param  arg2 Value to substitute for "{2}".
-     * @return The formatted string for the given key.
-     * @throws MissingResourceException If no object for the given key can be found.
-     */
-    public final String getString(final int    key,
-                                  final Object arg0,
-                                  final Object arg1,
-                                  final Object arg2) throws MissingResourceException
-    {
-        return getString(key, new Object[] {arg0, arg1, arg2});
-    }
-
-    /**
-     * Gets a string for the given key and replaces all occurrences of "{0}",
-     * "{1}", with values of {@code arg0}, {@code arg1}, etc.
-     *
-     * @param  key The key for the desired string.
-     * @param  arg0 Value to substitute for "{0}".
-     * @param  arg1 Value to substitute for "{1}".
-     * @param  arg2 Value to substitute for "{2}".
-     * @param  arg3 Value to substitute for "{3}".
-     * @return The formatted string for the given key.
-     * @throws MissingResourceException If no object for the given key can be found.
-     */
-    public final String getString(final int    key,
-                                  final Object arg0,
-                                  final Object arg1,
-                                  final Object arg2,
-                                  final Object arg3) throws MissingResourceException
-    {
-        return getString(key, new Object[] {arg0, arg1, arg2, arg3});
-    }
-
-    /**
-     * Gets a string for the given key and replaces all occurrences of "{0}",
-     * "{1}", with values of {@code arg0}, {@code arg1}, etc.
-     *
-     * @param  key The key for the desired string.
-     * @param  arg0 Value to substitute for "{0}".
-     * @param  arg1 Value to substitute for "{1}".
-     * @param  arg2 Value to substitute for "{2}".
-     * @param  arg3 Value to substitute for "{3}".
-     * @param  arg4 Value to substitute for "{4}".
-     * @return The formatted string for the given key.
-     * @throws MissingResourceException If no object for the given key can be found.
-     */
-    public final String getString(final int    key,
-                                  final Object arg0,
-                                  final Object arg1,
-                                  final Object arg2,
-                                  final Object arg3,
-                                  final Object arg4) throws MissingResourceException
-    {
-        return getString(key, new Object[] {arg0, arg1, arg2, arg3, arg4});
-    }
-
-    /**
-     * Gets a localized log record.
-     *
-     * @param  level The log record level.
-     * @param  key   The resource key.
-     * @return The log record.
-     */
-    public final LogRecord getLogRecord(final Level level, final int key) {
-        final LogRecord record = new LogRecord(level, getKeyNameAt(key));
-        record.setResourceBundleName(getClass().getName());
-        record.setResourceBundle(this);
-        return record;
-    }
-
-    /**
-     * Gets a localized log record.
-     *
-     * @param  level The log record level.
-     * @param  key   The resource key.
-     * @param  arg0  The parameter for the log message, which may be an array.
-     * @return The log record.
-     */
-    public final LogRecord getLogRecord(final Level level, final int key,
-                                        final Object arg0)
-    {
-        final LogRecord record = getLogRecord(level, key);
-        record.setParameters(toArray(arg0));
-        return record;
-    }
-
-    /**
-     * Gets a localized log record.
-     *
-     * @param  level The log record level.
-     * @param  key   The resource key.
-     * @param  arg0  The first parameter.
-     * @param  arg1  The second parameter.
-     * @return The log record.
-     */
-    public final LogRecord getLogRecord(final Level level, final int key,
-                                        final Object arg0,
-                                        final Object arg1)
-    {
-        return getLogRecord(level, key, new Object[] {arg0, arg1});
-    }
-
-    /**
-     * Gets a localized log record.
-     *
-     * @param  level The log record level.
-     * @param  key   The resource key.
-     * @param  arg0  The first parameter.
-     * @param  arg1  The second parameter.
-     * @param  arg2  The third parameter.
-     * @return The log record.
-     */
-    public final LogRecord getLogRecord(final Level level, final int key,
-                                        final Object arg0,
-                                        final Object arg1,
-                                        final Object arg2)
-    {
-        return getLogRecord(level, key, new Object[] {arg0, arg1, arg2});
-    }
-
-    /**
-     * Gets a localized log record.
-     *
-     * @param  level The log record level.
-     * @param  key   The resource key.
-     * @param  arg0  The first parameter.
-     * @param  arg1  The second parameter.
-     * @param  arg2  The third parameter.
-     * @param  arg3  The fourth parameter.
-     * @return The log record.
-     */
-    public final LogRecord getLogRecord(final Level level, final int key,
-                                        final Object arg0,
-                                        final Object arg1,
-                                        final Object arg2,
-                                        final Object arg3)
-    {
-        return getLogRecord(level, key, new Object[] {arg0, arg1, arg2, arg3});
-    }
-
-    /**
-     * Returns a string representation of this object.
-     * This method is for debugging purposes only.
-     *
-     * @return A string representation of this resources bundle.
+     * Returns the numerical value for the key of the given name.
      */
-    @Debug
-    @Override
-    public synchronized String toString() {
-        return getClass().getSimpleName() + '[' + getLocale() + ']';
+    final int getKeyValue(final String name) throws NoSuchFieldException, IllegalAccessException {
+        return (Integer) keysClass.getField(name).get(null);
     }
 }

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java?rev=1411581&r1=1411580&r2=1411581&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java Tue Nov 20 08:33:06 2012
@@ -41,7 +41,15 @@ public final class Messages extends Inde
      * @version 0.3
      * @module
      */
-    public static final class Keys {
+    public static final class Keys extends KeyConstants {
+        /**
+         * The unique instance of key constants handler.
+         */
+        static final Keys INSTANCE = new Keys();
+
+        /**
+         * For {@link #INSTANCE} creation only.
+         */
         private Keys() {
         }
 
@@ -66,12 +74,11 @@ public final class Messages extends Inde
     }
 
     /**
-     * Returns the {@code Keys} class.
+     * Returns the handle for the {@code Keys} constants.
      */
     @Override
-    final Class<?> getKeysClass() throws ClassNotFoundException {
-        assert super.getKeysClass() == Keys.class;
-        return Keys.class;
+    final KeyConstants getKeyConstants() {
+        return Keys.INSTANCE;
     }
 
     /**
@@ -174,9 +181,10 @@ public final class Messages extends Inde
     private static final class International extends ResourceInternationalString {
         private static final long serialVersionUID = -229348959712294903L;
 
-        International(int key)              {super(key);}
-        International(int key, Object args) {super(key, args);}
-        @Override IndexedResourceBundle getBundle(Locale locale) {
+        International(int key)                   {super(key);}
+        International(int key, Object args)      {super(key, args);}
+        @Override KeyConstants getKeyConstants() {return Keys.INSTANCE;}
+        @Override IndexedResourceBundle getBundle(final Locale locale) {
             return getResources(locale);
         }
     }

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/ResourceInternationalString.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/ResourceInternationalString.java?rev=1411581&r1=1411580&r2=1411581&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/ResourceInternationalString.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/ResourceInternationalString.java Tue Nov 20 08:33:06 2012
@@ -17,10 +17,15 @@
 package org.apache.sis.util.resources;
 
 import java.io.Serializable;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.InvalidObjectException;
+import java.io.IOException;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import net.jcip.annotations.Immutable;
 import org.apache.sis.util.Utilities;
+import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.type.AbstractInternationalString;
 
 // Related to JDK7
@@ -45,16 +50,16 @@ abstract class ResourceInternationalStri
     private static final long serialVersionUID = 4744571031462678126L;
 
     /**
-     * The key for the resource to fetch.
+     * The key for the resource to fetch. A negative value means that the resource takes no
+     * argument, in which case the {@link #arguments} field shall be ignored. Negative key
+     * values are converted to positive values using the {@code ~} operator.
      */
-    private final int key;
+    private transient int key;
 
     /**
-     * The argument(s), or {@code Loader.class} if none. The choice of {@code Loader.class} for
-     * meaning "no argument" is arbitrary - we just need a reference that the user is unlikely
-     * to known, and we need the referenced object to be serializable. We can not use the
-     * {@code null} value for "no argument" because the user may really wants to specify
-     * {@code null} as an argument value.
+     * The argument(s), or {@code null} if none. Note that the user may also really want to
+     * specify {@code null} as an argument value. We distinguish the two cases with the sign
+     * of the {@link #key} value.
      */
     private final Object arguments;
 
@@ -64,8 +69,9 @@ abstract class ResourceInternationalStri
      * @param key The key for the resource to fetch.
      */
     ResourceInternationalString(final int key) {
-        this.key  = key;
-        arguments = Loader.class;
+        ArgumentChecks.ensurePositive("key", key);
+        this.key  = ~key;
+        arguments = null;
     }
 
     /**
@@ -75,11 +81,21 @@ abstract class ResourceInternationalStri
      * @param The argument(s).
      */
     ResourceInternationalString(final int key, final Object arguments) {
+        ArgumentChecks.ensurePositive("key", key);
         this.key = key;
         this.arguments = arguments;
     }
 
     /**
+     * Returns a handler for the constants declared in the inner {@code Keys} class.
+     * This is used at serialization time in order to serialize the constant name
+     * rather than its numeric value.
+     *
+     * @return A handler for the constants declared in the inner {@code Keys} class.
+     */
+    abstract KeyConstants getKeyConstants();
+
+    /**
      * Returns the resource bundle for the given locale.
      *
      * @param  locale The locale for which to get the resource bundle.
@@ -104,8 +120,8 @@ abstract class ResourceInternationalStri
             locale = Locale.ENGLISH;
         }
         final IndexedResourceBundle resources = getBundle(locale);
-        return (arguments == Loader.class)
-                ? resources.getString(key)
+        return (key < 0)
+                ? resources.getString(~key)
                 : resources.getString(key, arguments);
     }
 
@@ -117,7 +133,7 @@ abstract class ResourceInternationalStri
      */
     @Override
     public boolean equals(final Object object) {
-        if (object instanceof ResourceInternationalString) {
+        if (object != null && object.getClass() == getClass()) {
             final ResourceInternationalString that = (ResourceInternationalString) object;
             return this.key == that.key && Objects.equals(this.arguments, that.arguments);
         }
@@ -131,6 +147,32 @@ abstract class ResourceInternationalStri
      */
     @Override
     public int hashCode() {
-        return key ^ Utilities.deepHashCode(arguments) ^ (int) serialVersionUID;
+        return getClass().hashCode() ^ (key + 31*Utilities.deepHashCode(arguments)) ^ (int) serialVersionUID;
+    }
+
+    /**
+     * Serializes this international string using the key name rather than numerical value.
+     */
+    private void writeObject(final ObjectOutputStream out) throws IOException {
+        out.defaultWriteObject();
+        out.writeUTF(getKeyConstants().getKeyName(key >= 0 ? key : ~key));
+        out.writeBoolean(key < 0);
+    }
+
+    /**
+     * Deserializes an object serialized by {@link #writeObject(ObjectOutputStream)}.
+     */
+    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        try {
+            key = getKeyConstants().getKeyValue(in.readUTF());
+        } catch (ReflectiveOperationException cause) {
+            InvalidObjectException e = new InvalidObjectException(cause.toString());
+            e.initCause(cause);
+            throw e;
+        }
+        if (in.readBoolean()) {
+            key = ~key;
+        }
     }
 }

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java?rev=1411581&r1=1411580&r2=1411581&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java Tue Nov 20 08:33:06 2012
@@ -41,7 +41,15 @@ public final class Vocabulary extends In
      * @version 0.3
      * @module
      */
-    public static final class Keys {
+    public static final class Keys extends KeyConstants {
+        /**
+         * The unique instance of key constants handler.
+         */
+        static final Keys INSTANCE = new Keys();
+
+        /**
+         * For {@link #INSTANCE} creation only.
+         */
         private Keys() {
         }
 
@@ -66,12 +74,11 @@ public final class Vocabulary extends In
     }
 
     /**
-     * Returns the {@code Keys} class.
+     * Returns the handle for the {@code Keys} constants.
      */
     @Override
-    final Class<?> getKeysClass() throws ClassNotFoundException {
-        assert super.getKeysClass() == Keys.class;
-        return Keys.class;
+    final KeyConstants getKeyConstants() {
+        return Keys.INSTANCE;
     }
 
     /**
@@ -102,9 +109,10 @@ public final class Vocabulary extends In
     private static final class International extends ResourceInternationalString {
         private static final long serialVersionUID = 8360132666298806838L;
 
-        International(int key)              {super(key);}
-        International(int key, Object args) {super(key, args);}
-        @Override IndexedResourceBundle getBundle(Locale locale) {
+        International(int key)                   {super(key);}
+        International(int key, Object args)      {super(key, args);}
+        @Override KeyConstants getKeyConstants() {return Keys.INSTANCE;}
+        @Override IndexedResourceBundle getBundle(final Locale locale) {
             return getResources(locale);
         }
     }

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/resources/IndexedResourceBundleTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/resources/IndexedResourceBundleTest.java?rev=1411581&r1=1411580&r2=1411581&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/resources/IndexedResourceBundleTest.java (original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/resources/IndexedResourceBundleTest.java Tue Nov 20 08:33:06 2012
@@ -30,7 +30,7 @@ import org.apache.sis.test.TestCase;
 import org.junit.Test;
 import org.junit.After;
 
-import static org.junit.Assert.*;
+import static org.apache.sis.test.Assert.*;
 
 
 /**
@@ -155,10 +155,12 @@ public final strictfp class IndexedResou
         InternationalString i18n = Errors.formatInternational(Errors.Keys.NullArgument_1);
         assertEquals("Argument ‘{0}’ shall not be null.",      i18n.toString(Locale.ENGLISH));
         assertEquals("L’argument ‘{0}’ ne doit pas être nul.", i18n.toString(Locale.FRENCH));
+        assertSerializedEquals(i18n);
 
         i18n = Errors.formatInternational(Errors.Keys.NullArgument_1, "CRS");
         assertEquals("Argument ‘CRS’ shall not be null.",      i18n.toString(Locale.ENGLISH));
         assertEquals("L’argument ‘CRS’ ne doit pas être nul.", i18n.toString(Locale.FRENCH));
+        assertSerializedEquals(i18n);
     }
 
     /**



Mime
View raw message