sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1385276 - in /incubator/sis/trunk: sis-build-helper/src/main/java/org/apache/sis/resources/ sis-utility/src/main/java/org/apache/sis/resources/
Date Sun, 16 Sep 2012 14:55:11 GMT
Author: desruisseaux
Date: Sun Sep 16 14:55:10 2012
New Revision: 1385276

URL: http://svn.apache.org/viewvc?rev=1385276&view=rev
Log:
Initial commit of IndexedResourceBundle, with empty (for now) Errors resources.

Added:
    incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.java   (with props)
    incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.properties   (with props)
    incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors_fr.properties   (with props)
    incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/IndexedResourceBundle.java   (with props)
    incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Loader.java   (with props)
    incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/package-info.java   (with props)
Modified:
    incubator/sis/trunk/sis-build-helper/src/main/java/org/apache/sis/resources/IndexedResourceCompiler.java

Modified: incubator/sis/trunk/sis-build-helper/src/main/java/org/apache/sis/resources/IndexedResourceCompiler.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-build-helper/src/main/java/org/apache/sis/resources/IndexedResourceCompiler.java?rev=1385276&r1=1385275&r2=1385276&view=diff
==============================================================================
--- incubator/sis/trunk/sis-build-helper/src/main/java/org/apache/sis/resources/IndexedResourceCompiler.java (original)
+++ incubator/sis/trunk/sis-build-helper/src/main/java/org/apache/sis/resources/IndexedResourceCompiler.java Sun Sep 16 14:55:10 2012
@@ -49,9 +49,9 @@ public class IndexedResourceCompiler imp
 
     /**
      * Prefix for argument count in resource key names. For example, a resource
-     * expecting one argument may have a key name like "HELLO_p1".
+     * expecting one argument may have a key name like "{@code HelloWorld_1}".
      */
-    private static final String ARGUMENT_COUNT_PREFIX = "_p";
+    private static final String ARGUMENT_COUNT_PREFIX = "_";
 
     /**
      * The maximal length of comment lines.

Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.java?rev=1385276&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.java Sun Sep 16 14:55:10 2012
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.resources;
+
+import java.util.Locale;
+import java.util.MissingResourceException;
+
+
+/**
+ * Locale-dependent resources for error messages.
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @since   0.3 (derived from geotk-2.2)
+ * @version 0.3
+ * @module
+ */
+public final class Errors extends IndexedResourceBundle {
+    /**
+     * Resource keys. This class is used when compiling sources, but no dependencies to
+     * {@code Keys} should appear in any resulting class files. Since the Java compiler
+     * inlines final integer values, using long identifiers will not bloat the constant
+     * pools of compiled classes.
+     *
+     * @author  Martin Desruisseaux (IRD, Geomatys)
+     * @since   0.3 (derived from geotk-2.2)
+     * @version 0.3
+     * @module
+     */
+    public static final class Keys {
+        private Keys() {
+        }
+    }
+
+    /**
+     * Constructs a new resource bundle loading data from the given UTF file.
+     *
+     * @param filename The file or the JAR entry containing resources.
+     */
+    Errors(final String filename) {
+        super(filename);
+    }
+
+    /**
+     * Returns resources in the given locale.
+     *
+     * @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.
+     */
+    public static Errors getResources(final Locale locale) throws MissingResourceException {
+        return getBundle(Errors.class, locale);
+    }
+
+    /**
+     * 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 static String format(final int key) throws MissingResourceException {
+        return getResources(null).getString(key);
+    }
+
+    /**
+     * Gets a string for the given key are replace all occurrence of "{0}"
+     * with values of {@code arg0}.
+     *
+     * @param  key The key for the desired string.
+     * @param  arg0 Value to substitute to "{0}".
+     * @return The formatted string for the given key.
+     * @throws MissingResourceException If no object for the given key can be found.
+     */
+    public static String format(final int    key,
+                                final Object arg0) throws MissingResourceException
+    {
+        return getResources(null).getString(key, arg0);
+    }
+
+    /**
+     * Gets a string for the given key are replace all occurrence of "{0}",
+     * "{1}", with values of {@code arg0}, {@code arg1}.
+     *
+     * @param  key The key for the desired string.
+     * @param  arg0 Value to substitute to "{0}".
+     * @param  arg1 Value to substitute to "{1}".
+     * @return The formatted string for the given key.
+     * @throws MissingResourceException If no object for the given key can be found.
+     */
+    public static String format(final int    key,
+                                final Object arg0,
+                                final Object arg1) throws MissingResourceException
+    {
+        return getResources(null).getString(key, arg0, arg1);
+    }
+
+    /**
+     * Gets a string for the given key are replace all occurrence of "{0}",
+     * "{1}", with values of {@code arg0}, {@code arg1}, etc.
+     *
+     * @param  key The key for the desired string.
+     * @param  arg0 Value to substitute to "{0}".
+     * @param  arg1 Value to substitute to "{1}".
+     * @param  arg2 Value to substitute to "{2}".
+     * @return The formatted string for the given key.
+     * @throws MissingResourceException If no object for the given key can be found.
+     */
+    public static String format(final int    key,
+                                final Object arg0,
+                                final Object arg1,
+                                final Object arg2) throws MissingResourceException
+    {
+        return getResources(null).getString(key, arg0, arg1, arg2);
+    }
+
+    /**
+     * Gets a string for the given key are replace all occurrence of "{0}",
+     * "{1}", with values of {@code arg0}, {@code arg1}, etc.
+     *
+     * @param  key The key for the desired string.
+     * @param  arg0 Value to substitute to "{0}".
+     * @param  arg1 Value to substitute to "{1}".
+     * @param  arg2 Value to substitute to "{2}".
+     * @param  arg3 Value to substitute to "{3}".
+     * @return The formatted string for the given key.
+     * @throws MissingResourceException If no object for the given key can be found.
+     */
+    public static String format(final int    key,
+                                final Object arg0,
+                                final Object arg1,
+                                final Object arg2,
+                                final Object arg3) throws MissingResourceException
+    {
+        return getResources(null).getString(key, arg0, arg1, arg2, arg3);
+    }
+}

Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.properties
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.properties?rev=1385276&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.properties (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.properties Sun Sep 16 14:55:10 2012
@@ -0,0 +1,16 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#

Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors.properties
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors_fr.properties?rev=1385276&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors_fr.properties (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors_fr.properties Sun Sep 16 14:55:10 2012
@@ -0,0 +1,16 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#

Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors_fr.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Errors_fr.properties
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/IndexedResourceBundle.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/IndexedResourceBundle.java?rev=1385276&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/IndexedResourceBundle.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/IndexedResourceBundle.java Sun Sep 16 14:55:10 2012
@@ -0,0 +1,772 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.resources;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.text.MessageFormat;
+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 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.CharSequences;
+
+
+/**
+ * {@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}.
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @since   0.3 (derived from geotk-1.2)
+ * @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;
+
+    /**
+     * 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}.
+     */
+    private final String filename;
+
+    /**
+     * 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.
+     */
+    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
+     * @see Loggings#getResources
+     * @see Errors#getResources
+     */
+    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;
+    }
+
+    /**
+     * 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 Writer out) throws IOException {
+        // Synchronization performed by 'ensureLoaded'
+        list(out, ensureLoaded(null));
+    }
+
+    /**
+     * 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.
+     * @param  values The resources to list.
+     * @throws IOException if an output operation failed.
+     */
+    private static void list(final Writer out, final String[] values) throws IOException {
+        final String lineSeparator = System.getProperty("line.separator", "\n");
+        for (int i=0; i<values.length; i++) {
+            String value = values[i];
+            if (value == null) {
+                continue;
+            }
+            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);
+// TODO     out.write(CharSequences.spaces(5 - number.length()));
+            out.write(number);
+            out.write(":\t");
+            out.write(value, 0, Math.min(indexCR,indexLF));
+            out.write(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();
+                }
+                final DataInputStream input = new DataInputStream(new BufferedInputStream(in));
+                try {
+                    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;
+                        }
+                    }
+                } finally {
+                    input.close();
+                }
+                /*
+                 * 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()});
+            }
+// TODO     Logging.log(IndexedResourceBundle.class, methodName, record);
+            return values;
+        } catch (IOException exception) {
+            record.setLevel  (Level.WARNING);
+            record.setMessage(exception.getLocalizedMessage());
+            record.setThrown (exception);
+// TODO     Logging.log(IndexedResourceBundle.class, methodName, record);
+            final MissingResourceException error = new MissingResourceException(
+                    exception.getLocalizedMessage(), getClass().getCanonicalName(), key);
+            error.initCause(exception);
+            throw error;
+        }
+    }
+
+    /**
+     * Returns an enumeration of the keys.
+     *
+     * @return All keys in this resource bundle.
+     */
+    @Override
+    public final Enumeration<String> getKeys() {
+        // Synchronization performed by 'ensureLoaded'
+        return new KeyEnum(ensureLoaded(null));
+    }
+
+    /**
+     * The keys as an enumeration.
+     */
+    private static final class KeyEnum implements Enumeration<String> {
+        private final String[] values;
+        private int next=0;
+
+        KeyEnum(final String[] values) {
+            this.values = values;
+            while (next < values.length) {
+                if (values[next] != null) {
+                    break;
+                }
+                next++;
+            }
+        }
+
+        @Override
+        public boolean hasMoreElements() {
+            return next < values.length;
+        }
+
+        @Override
+        public String nextElement() {
+            while (next < values.length) {
+                if (values[next] != null) {
+                    return String.valueOf(next++);
+                }
+                next++;
+            }
+            throw new NoSuchElementException();
+        }
+    }
+
+    /**
+     * 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);
+        final int keyID;
+        try {
+            keyID = Integer.parseInt(key);
+        } catch (NumberFormatException exception) {
+            return null; // This is okay as of 'handleGetObject' contract.
+        }
+        return (keyID >= 0 && keyID < values.length) ? values[keyID] : null;
+    }
+
+    /**
+     * Makes sure that the {@code text} string is not longer than {@code maxLength} characters.
+     * If {@code text} is not longer, it is returned unchanged (except for trailing blanks,
+     * which are removed). If {@code text} is longer, it will be cut somewhere in the middle.
+     * This method tries to cut between two words and replace the missing words with "(...)".
+     * For example, the following string:
+     *
+     * <blockquote>
+     *   "This sentence given as an example is way too long to be
+     *    included in a message."
+     * </blockquote>
+     *
+     * May be "summarized" by something like this:
+     *
+     * <blockquote>
+     *   "This sentence given (...) included in a message."
+     * </blockquote>
+     *
+     * @param  text The sentence to summarize if it is too long.
+     * @param  maxLength The maximum length allowed for {@code text}.
+     *         If {@code text} is longer, it will be summarized.
+     * @return A sentence not longer than {@code maxLength}.
+     */
+    private static String summarize(String text, int maxLength) {
+// TODO text = CharSequences.trim(text);
+        final int length = text.length();
+        if (length <= maxLength) {
+            return text;
+        }
+        /*
+         * Computes maximum length for one half of the string. Take into
+         * account the space needed for inserting the " (...) " string.
+         */
+        maxLength = (maxLength - 7) >> 1;
+        if (maxLength <= 0) {
+            return text;
+        }
+        /*
+         * We will remove characters from 'break1' to 'break2', both exclusive. We try to adjust
+         * 'break1' and 'break2' in such a way that the first and last characters to be removed
+         * will be spaces or punctuation characters. Constants 'lower' and 'upper' are limit
+         * values. If we don't find values for 'break1' and 'break2' inside those limits, we
+         * will give up.
+         */
+        int break1 = maxLength;
+        int break2 = length - maxLength;
+        for (final int lower = (maxLength >> 1); break1 >= lower; break1--) {
+            if (!Character.isUnicodeIdentifierPart(text.charAt(break1))) {
+                while (--break1>=lower && !Character.isUnicodeIdentifierPart(text.charAt(break1)));
+                break;
+            }
+        }
+        for (final int upper = length - (maxLength >> 1); break2 < upper; break2++) {
+            if (!Character.isUnicodeIdentifierPart(text.charAt(break2))) {
+                while (++break2<upper && !Character.isUnicodeIdentifierPart(text.charAt(break2)));
+                break;
+            }
+        }
+        return /*CharSequences.trim TODO */(new StringBuilder(break1 + (length-break2) + 6)
+                .append(text, 0, break1+1).append(" (…) ").append(text, break2, length).toString());
+    }
+
+    /**
+     * Returns {@code arguments} as an array. If {@code arguments} is already an array, this array
+     * or a copy of this array will be returned. If {@code arguments} is not an array, it will be
+     * placed in an array of length 1. In any case, all the array elements will be checked for
+     * {@link String} objects. Any strings of length greater than {@link #MAX_STRING_LENGTH} will
+     * be reduced using the {@link #summarize} method.
+     *
+     * @param  arguments The object to check.
+     * @return {@code arguments} as an array.
+     */
+    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];
+            if (element instanceof CharSequence) {
+                final String s0;
+                if (element instanceof InternationalString) {
+                    s0 = ((InternationalString) element).toString(getFormatLocale());
+                } else {
+                    s0 = element.toString();
+                }
+                final String s1 = summarize(s0, MAX_STRING_LENGTH);
+                if (!s0.equals(s1)) {
+                    if (array == arguments) {
+                        array = new Object[array.length];
+                        System.arraycopy(arguments, 0, array, 0, array.length);
+                    }
+                    array[i] = s1;
+                }
+            } else if (element instanceof Throwable) {
+                String message = ((Throwable) element).getLocalizedMessage();
+                if (message == null) {
+                    message = element.getClass().getSimpleName(); // TODO Classes.getShortClassName(element);
+                }
+                array[i] = message;
+            } else if (element instanceof Class<?>) {
+                array[i] = ((Class<?>) element).getSimpleName(); // TODO Classes.getShortName((Class<?>) element);
+            }
+        }
+        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());
+            } 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 LogRecord getLogRecord(final Level level, final int key) {
+        return getLogRecord(level, key, null);
+    }
+
+    /**
+     * Gets a localized log record.
+     *
+     * @param  level The log record level.
+     * @param  key   The resource key.
+     * @param  arg0  The parameter for the log message, or {@code null}.
+     * @return The log record.
+     */
+    public LogRecord getLogRecord(final Level level, final int key,
+                                  final Object arg0)
+    {
+        final LogRecord record = new LogRecord(level, String.valueOf(key));
+        record.setResourceBundle(this);
+        if (arg0 != null) {
+            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 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 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 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});
+    }
+
+    /**
+     * Localizes and formats the message string from a log record. This method performs a work
+     * similar to {@link java.util.logging.Formatter#formatMessage}, except that the work will be
+     * delegated to {@link #getString(int, Object)} if the {@linkplain LogRecord#getResourceBundle
+     * record resource bundle} is an instance of {@code IndexedResourceBundle}.
+     *
+     * @param  record The log record to format.
+     * @return The formatted message.
+     */
+    public static String format(final LogRecord record) {
+        String message = record.getMessage();
+        final ResourceBundle resources = record.getResourceBundle();
+        if (resources instanceof IndexedResourceBundle) {
+            int key = -1;
+            try {
+                key = Integer.parseInt(message);
+            } catch (NumberFormatException e) {
+                 unexpectedException(e);
+            }
+            if (key >= 0) {
+                final Object[] parameters = record.getParameters();
+                return ((IndexedResourceBundle) resources).getString(key, parameters);
+            }
+        }
+        if (resources != null) {
+            try {
+                message = resources.getString(message);
+            } catch (MissingResourceException e) {
+                unexpectedException(e);
+            }
+            final Object[] parameters = record.getParameters();
+            if (parameters != null && parameters.length != 0) {
+                final int offset = message.indexOf('{');
+                if (offset >= 0 && offset < message.length()-1) {
+                    // Uses a more restrictive check than Character.isDigit(char)
+                    final char c = message.charAt(offset);
+                    if (c>='0' && c<='9') try {
+                        return MessageFormat.format(message, parameters);
+                    } catch (IllegalArgumentException e) {
+                        unexpectedException(e);
+                    }
+                }
+            }
+        }
+        return message;
+    }
+
+    /**
+     * Invoked when an unexpected exception occurred in the {@link #format} method.
+     */
+    private static void unexpectedException(final RuntimeException exception) {
+// TODO Logging.unexpectedException(IndexedResourceBundle.class, "format", exception);
+    }
+
+    /**
+     * Returns a string representation of this object.
+     * This method is for debugging purposes only.
+     *
+     * @return A string representation of this resources bundle.
+     */
+    @Override
+    public synchronized String toString() {
+        return getClass().getSimpleName() + '[' + getLocale() + ']';
+    }
+}

Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/IndexedResourceBundle.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/IndexedResourceBundle.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Loader.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Loader.java?rev=1385276&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Loader.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Loader.java Sun Sep 16 14:55:10 2012
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.resources;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Collections;
+import java.util.ResourceBundle;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.io.IOException;
+
+
+/**
+ * Controls the resource bundle loading. This class looks for {@code .utf} files rather than
+ * the Java default {@code .class} or {@code .properties} files.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-3.00)
+ * @version 0.3
+ * @module
+ */
+final class Loader extends ResourceBundle.Control {
+    /**
+     * The filename extension of resource files, without leading dot.
+     */
+    private static final String EXTENSION = "utf";
+
+    /**
+     * The formats supported by this loader.
+     */
+    private static final List<String> FORMATS = Collections.singletonList("apache-sis." + EXTENSION);
+
+    /**
+     * The singleton instance of the {@link Loader} class.
+     */
+    public static final Loader INSTANCE = new Loader();
+
+    /**
+     * Creates the unique instance of the SIS resource bundle loader.
+     */
+    private Loader() {
+    }
+
+    /**
+     * Returns the formats supported by this loader.
+     * The only supported format is {@code "apache-sis.utf"}.
+     *
+     * @param  baseName Ignored.
+     * @return The supported formats.
+     */
+    @Override
+    public List<String> getFormats(String baseName) {
+        return FORMATS;
+    }
+
+    /**
+     * Returns {@code false} in all cases, since our implementation never needs reload.
+     */
+    @Override
+    public boolean needsReload(final String baseName, final Locale locale, final String format,
+            final ClassLoader loader, final ResourceBundle bundle, long loadTime)
+    {
+        return false;
+    }
+
+    /**
+     * Instantiates a new resource bundle.
+     *
+     * @param  baseName  The fully qualified name of the base resource bundle.
+     * @param  locale    The locale for which the resource bundle should be instantiated.
+     * @param  format    Ignored since this loader supports only one format.
+     * @param  loader    The class loader to use.
+     * @param  reload    Ignored since this loader do not supports resource expiration.
+     * @return The resource bundle instance, or null if none could be found.
+     */
+    @Override
+    public ResourceBundle newBundle(final String baseName, final Locale locale,
+            final String format, final ClassLoader loader, final boolean reload)
+            throws IllegalAccessException, InstantiationException, IOException
+    {
+        final Class<?> classe;
+        try {
+            classe = Class.forName(baseName, true, loader);
+        } catch (ClassNotFoundException e) {
+            return null; // This is the expected behavior as of Control.newBundle contract.
+        }
+        /*
+         * Gets the filename relative to the class we created, since we assumes that UTF files
+         * are in the same package. Then check for file existence and instantiate the resource
+         * bundle only if the file is found.
+         */
+        final String classname = classe.getSimpleName();
+        String filename = toResourceName(toBundleName(classname, locale), EXTENSION);
+        if (classe.getResource(filename) == null) {
+            if (!Locale.ENGLISH.equals(locale)) {
+                return null;
+            }
+            // We have no explicit resources for English. We use the default one for that.
+            filename = toResourceName(classname, EXTENSION);
+            if (classe.getResource(filename) == null) {
+                return null;
+            }
+        }
+        /*
+         * If the file exists, instantiate now the resource bundle. Note that the constructor
+         * will not loads the data immediately, which is why we don't pass it the above URL.
+         */
+        final Constructor<?> c;
+        try {
+            c = classe.getDeclaredConstructor(String.class);
+        } catch (NoSuchMethodException e) {
+            throw instantiationFailure(e);
+        }
+        final ResourceBundle bundle;
+        // Do not call c.setAccessible(true) - this is not allowed in Applet.
+        try {
+            bundle = (ResourceBundle) c.newInstance(filename);
+        } catch (InvocationTargetException e) {
+            throw instantiationFailure(e);
+        }
+        return bundle;
+    }
+
+    /**
+     * Creates an exception for a resource bundle that can not be created.
+     */
+    private static InstantiationException instantiationFailure(final Exception cause) {
+        InstantiationException exception = new InstantiationException(cause.getLocalizedMessage());
+        exception.initCause(cause);
+        return exception;
+    }
+}

Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Loader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/Loader.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/package-info.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/package-info.java?rev=1385276&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/package-info.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/package-info.java Sun Sep 16 14:55:10 2012
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Localized resources for SIS.
+ *
+ * <STRONG>Do not use!</STRONG>
+ *
+ * This package is for internal use by SIS only. Classes in this package
+ * may change in incompatible ways in any future version without notice.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-1.2)
+ * @version 0.3
+ * @module
+ */
+package org.apache.sis.resources;

Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/package-info.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/resources/package-info.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message