You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2012/11/20 09:33:07 UTC
svn commit: r1411581 - in /sis/branches/JDK7/sis-utility/src:
main/java/org/apache/sis/util/resources/
test/java/org/apache/sis/util/resources/
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);
}
/**