sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1391395 - in /sis/trunk/sis-utility/src/main/java/org/apache/sis/util: Locales.java Static.java package-info.java resources/Errors.java resources/Errors.properties resources/Errors_fr.properties resources/package-info.java
Date Fri, 28 Sep 2012 11:01:35 GMT
Author: desruisseaux
Date: Fri Sep 28 11:01:34 2012
New Revision: 1391395

URL: http://svn.apache.org/viewvc?rev=1391395&view=rev
Log:
Added utility methods working on java.util.Locale.

Added:
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Locales.java   (with props)
Modified:
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Static.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/package-info.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/package-info.java

Added: sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Locales.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Locales.java?rev=1391395&view=auto
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Locales.java (added)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Locales.java Fri Sep 28 11:01:34
2012
@@ -0,0 +1,306 @@
+/*
+ * 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.util;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.MissingResourceException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Errors;
+
+import static org.apache.sis.util.Arrays.resize;
+import static org.apache.sis.util.collection.Collections.hashMapCapacity;
+
+
+/**
+ * Utilities methods working on {@link Locale} instances. While this class is documented
as
+ * providing static methods, a few methods are actually non-static. Those methods need to
be
+ * invoked on the {@link #SYSTEM} or {@link #LIBRARY} instance in order to specify the scope.
+ * Examples:
+ *
+ * {@preformat java
+ *     Locales[] lc1 = Locales.SYSTEM .getAvailableLanguages();  // All languages installed
on the JavaVM.
+ *     Locales[] lc2 = Locales.LIBRARY.getAvailableLanguages();  // Only the languages known
to Apache SIS.
+ * }
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @since   0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ */
+public final class Locales extends Static {
+    /**
+     * A read-only map for canonicalizing the locales. Filled on class
+     * initialization in order to avoid the need for synchronization.
+     */
+    private static final Map<Locale,Locale> POOL;
+    static {
+        final Locale[] locales = Locale.getAvailableLocales();
+        POOL = new HashMap<Locale,Locale>(hashMapCapacity(locales.length));
+        for (final Locale lc : locales) {
+            POOL.put(lc, lc);
+        }
+        /*
+         * Add the static field constants. This operation may replace some values which
+         * were returned by Locale.getAvailableLocales(). This is the desired effect,
+         * since we want to give precedence to references to the static constants.
+         */
+        try {
+            final Field[] fields = Locale.class.getFields();
+            for (int i=0; i<fields.length; i++) {
+                final Field field = fields[i];
+                if (Modifier.isStatic(field.getModifiers())) {
+                    if (Locale.class.isAssignableFrom(field.getType())) {
+                        final Locale toAdd = (Locale) field.get(null);
+                        POOL.put(toAdd, toAdd);
+                    }
+                }
+            }
+        } catch (Exception exception) { // Too many exceptions for enumerating them.
+            /*
+             * Not a big deal if this operation fails (this is actually just an
+             * optimization for reducing memory usage). Log a warning and stop.
+             */
+            Logging.unexpectedException(Locales.class, "<clinit>", exception);
+        }
+    }
+
+    /**
+     * All locales available on the JavaVM.
+     */
+    public final Locales SYSTEM = new Locales();
+
+    /**
+     * Only locales available in the Apache SIS library. They are the locales for which localized
+     * resources are provided in the {@link org.apache.sis.util.resources} package.
+     */
+    public final Locales LIBRARY = new Locales();
+
+    /**
+     * Do not allow instantiation of this class,
+     * except for the constants defined in this class.
+     */
+    private Locales() {
+    }
+
+    /**
+     * Returns the languages known to the JavaVM ({@link #SYSTEM}) or to the Apache SIS library
+     * ({@link #LIBRARY}). In the later case, this method returns only the languages for
which
+     * localized resources are provided in the {@link org.apache.sis.util.resources} package.
+     *
+     * @return The list of supported languages.
+     */
+    public Locale[] getAvailableLanguages() {
+        if (this == SYSTEM) {
+            return getLanguages(Locale.getAvailableLocales());
+        }
+        return new Locale[] {
+            Locale.ENGLISH,
+            Locale.FRENCH
+        };
+    }
+
+    /**
+     * Returns the locales known to the JavaVM ({@link #SYSTEM}) or to the Apache SIS library
+     * ({@link #LIBRARY}). In the later case, this method returns only the locales for which
+     * localized resources are provided in the {@link org.apache.sis.util.resources} package.
+     *
+     * @return The list of supported locales.
+     */
+    public Locale[] getAvailableLocales() {
+        if (this == SYSTEM) {
+            return Locale.getAvailableLocales();
+        }
+        final Locale[] languages = getAvailableLanguages();
+        Locale[] locales = Locale.getAvailableLocales();
+        int count = 0;
+        for (int i=0; i<locales.length; i++) {
+            final Locale locale = locales[i];
+            if (containsLanguage(languages, locale)) {
+                locales[count++] = unique(locale);
+            }
+        }
+        locales = resize(locales, count);
+        return locales;
+    }
+
+    /**
+     * Returns {@code true} if the specified array of locales contains at least
+     * one element with the specified language.
+     */
+    private static boolean containsLanguage(final Locale[] locales, final Locale language)
{
+        final String code = language.getLanguage();
+        for (int i=0; i<locales.length; i++) {
+            if (code.equals(locales[i].getLanguage())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the list of {@linkplain #getAvailableLocales() available locales} formatted
+     * as string in the specified locale.
+     *
+     * @param  locale The locale to use for formatting the strings to be returned.
+     * @return String descriptions of available locales.
+     */
+    public String[] getAvailableLocales(final Locale locale) {
+        final Locale[] locales = getAvailableLocales();
+        final String[] display = new String[locales.length];
+        for (int i=0; i<locales.length; i++) {
+            display[i] = locales[i].getDisplayName(locale);
+        }
+        Arrays.sort(display);
+        return display;
+    }
+
+    /**
+     * Returns the languages of the given locales, without duplicated values.
+     * The instances returned by this method have no country and no variant information.
+     *
+     * @param  locales The locales from which to get the languages.
+     * @return The languages, without country or variant information.
+     */
+    public static Locale[] getLanguages(final Locale... locales) {
+        final Set<String> codes = new LinkedHashSet<String>(hashMapCapacity(locales.length));
+        for (final Locale locale : locales) {
+            codes.add(locale.getLanguage());
+        }
+        int i=0;
+        final Locale[] languages = new Locale[codes.size()];
+        for (final String code : codes) {
+            languages[i++] = unique(new Locale(code));
+        }
+        return languages;
+    }
+
+    /**
+     * Returns the 3-letters ISO language code if available, or the 2-letters code otherwise.
+     *
+     * @param  locale The locale for which we want the language.
+     * @return The language code, 3 letters if possible or 2 letters otherwise.
+     */
+    public static String getLanguageCode(final Locale locale) {
+        try {
+            return locale.getISO3Language();
+        } catch (MissingResourceException e) {
+            Logging.recoverableException(Locales.class, "getLanguage", e);
+            return locale.getLanguage();
+        }
+    }
+
+    /**
+     * Parses the given locale. The string is the language code either as the 2 letters or
the
+     * 3 letters ISO code. It can optionally be followed by the {@code '_'} character and
the
+     * country code (again either as 2 or 3 letters), optionally followed by {@code '_'}
and
+     * the variant.
+     *
+     * @param  code The language code, which may be followed by country code.
+     * @return The language for the given code.
+     * @throws IllegalArgumentException If the given code doesn't seem to be a valid locale.
+     */
+    public static Locale parse(final String code) throws IllegalArgumentException {
+        final String language, country, variant;
+        int ci = code.indexOf('_');
+        if (ci < 0) {
+            language = code.trim();
+            country  = "";
+            variant  = "";
+        } else {
+            language = code.substring(0, ci).trim();
+            int vi = code.indexOf('_', ++ci);
+            if (vi < 0) {
+                country = code.substring(ci).trim();
+                variant = "";
+            } else {
+                country = code.substring(ci, vi).trim();
+                variant = code.substring(++vi).trim();
+                if (code.indexOf('_', vi) >= 0) {
+                    throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalLanguageCode_1,
code));
+                }
+            }
+        }
+        final boolean language3 = isThreeLetters(language);
+        final boolean country3  = isThreeLetters(country);
+        /*
+         * Perform a linear scan only if we need to compare some 3-letters ISO code.
+         * Otherwise (if every code are 2 letters), it will be faster to create a new
+         * locale and check for an existing instance in the hash map.
+         */
+        if (language3 || country3) {
+            String language2 = language;
+            String country2  = country;
+            for (Locale locale : Locale.getAvailableLocales()) {
+                String c = (language3) ? locale.getISO3Language() : locale.getLanguage();
+                if (language.equals(c)) {
+                    if (country2 == country) { // Really identity comparison.
+                        // Remember the 2-letters ISO code in an opportunist way.
+                        // If the 2-letters ISO code has been set for the country
+                        // as well, we will not change the language code because
+                        // it has already been set with the code associated with
+                        // the right country.
+                        language2 = locale.getLanguage();
+                    }
+                    c = (country3) ? locale.getISO3Country() : locale.getCountry();
+                    if (country.equals(c)) {
+                        country2 = locale.getCountry();
+                        if (variant.equals(locale.getVariant())) {
+                            return unique(locale);
+                        }
+                    }
+                }
+            }
+            return unique(new Locale(language2, country2, variant));
+        }
+        return unique(new Locale(language, country, variant));
+    }
+
+    /**
+     * Returns {@code true} if the following code is 3 letters, or {@code false} if 2 letters.
+     */
+    private static boolean isThreeLetters(final String code) {
+        switch (code.length()) {
+            case 0: // fall through
+            case 2: return false;
+            case 3: return true;
+            default: {
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalLanguageCode_1,
code));
+            }
+        }
+    }
+
+    /**
+     * Returns a unique instance of the given locale, if one is available.
+     * Otherwise returns the {@code locale} unchanged.
+     *
+     * @param  locale The locale to canonicalize.
+     * @return A unique instance of the given locale, or {@code locale} if
+     *         the given locale is not cached.
+     */
+    public static Locale unique(final Locale locale) {
+        final Locale candidate = POOL.get(locale);
+        return (candidate != null) ? candidate : locale;
+    }
+}

Propchange: sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Locales.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Locales.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Static.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Static.java?rev=1391395&r1=1391394&r2=1391395&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Static.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Static.java Fri Sep 28 11:01:34
2012
@@ -22,9 +22,11 @@ package org.apache.sis.util;
  * purpose only. The list below summarizes some of the utility classes:
  *
  * <table class="sis">
- * <tr><th colspan="2">Primitives and classes</th></tr>
+ * <tr><th colspan="2">Basic classes of the Java language</th></tr>
  * <tr><td>{@link CharSequences}</td>
  *     <td>Methods working on {@link CharSequence} or {@link String} instances.</td></tr>
+ * <tr><td>{@link StringBuilders}</td>
+ *     <td>Methods modifying {@link StringBuilder} content in-place.</td></tr>
  * <tr><td>{@link Numbers}</td>
  *     <td>Conversions between different kind of {@link Number}.</td></tr>
  * <tr><td>{@link Classes}</td>

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/util/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/package-info.java?rev=1391395&r1=1391394&r2=1391395&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/util/package-info.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/util/package-info.java Fri Sep 28 11:01:34
2012
@@ -24,6 +24,7 @@
  *     <li>Primitive or basic Java types:
  *         {@link org.apache.sis.util.CharSequences},
  *         {@link org.apache.sis.util.StringBuilders},
+ *         {@link org.apache.sis.util.Locales},
  *         {@link org.apache.sis.util.Numbers},
  *         {@link org.apache.sis.util.Classes}.</li>
  *     <li>Arrays:

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1391395&r1=1391394&r2=1391395&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java Fri Sep
28 11:01:34 2012
@@ -61,6 +61,11 @@ public final class Errors extends Indexe
         public static final int IllegalClass_2 = 3;
 
         /**
+         * The “{0}” language is not recognized.
+         */
+        public static final int IllegalLanguageCode_1 = 12;
+
+        /**
          * Range [{0} … {1}] is not valid.
          */
         public static final int IllegalRange_2 = 11;

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1391395&r1=1391394&r2=1391395&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties Fri
Sep 28 11:01:34 2012
@@ -17,6 +17,7 @@
 EmptyArgument_1=Argument \u2018{0}\u2019 shall not be empty.
 IllegalArgumentClass_3=Argument \u2018{0}\u2019 can not be an instance of \u2018{1}\u2019.
Expected an instance of \u2018{2}\u2019 or derived type.
 IllegalClass_2=Class \u2018{0}\u2019 is illegal. It must be \u2018{1}\u2019 or a derived
class.
+IllegalLanguageCode_1=The \u201c{0}\u201d language is not recognized.
 IllegalRange_2=Range [{0} \u2026 {1}] is not valid.
 IndexOutOfBounds_1=Index {0} is out of bounds.
 NegativeArgument_2=Argument \u2018{0}\u2019 shall not be negative. The given value was {1}.

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1391395&r1=1391394&r2=1391395&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
(original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
Fri Sep 28 11:01:34 2012
@@ -17,6 +17,7 @@
 EmptyArgument_1=L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre vide.
 IllegalArgumentClass_3=L\u2019argument \u2018{0}\u2019 ne peut pas \u00eatre de type \u2018{1}\u2019.
Une instance de \u2018{2}\u2019 ou d\u2019un type d\u00e9riv\u00e9 \u00e9tait attendue.
 IllegalClass_2=La classe \u2018{0}\u2019 est ill\u00e9gale. Il doit s\u2019agir d\u2019une
classe \u2018{1}\u2019 ou d\u00e9riv\u00e9e.
+IllegalLanguageCode_1=Le code de langue \u201c{0}\u201d n\u2019est pas reconnu.
 IllegalRange_2=La plage [{0} \u2026 {1}] n\u2019est pas valide.
 IndexOutOfBounds_1=L\u2019index {0} est en dehors des limites permises.
 NegativeArgument_2=L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre n\u00e9gatif. La
valeur donn\u00e9e \u00e9tait {1}.

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/package-info.java?rev=1391395&r1=1391394&r2=1391395&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/package-info.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/package-info.java Fri
Sep 28 11:01:34 2012
@@ -39,6 +39,19 @@
  * necessary. This avoid the unfortunate confusion documented in the warning section of
  * {@link java.text.MessageFormat} javadoc.</p>
  *
+ * {@section Usage}
+ * All {@link org.apache.sis.util.resources.IndexedResourceBundle} subclasses provide a
+ * {@code getResources(Locale)} static method. It can be used for fetching localized strings
+ * as below:
+ *
+ * {@preformat java
+ *     String text = TheBundle.getResources(locale).getString(key, optionalArguments);
+ * }
+ *
+ * For convenience, all {@code IndexedResourceBundle} subclass provide also various
+ * {@code format(int, …)} static methods for fetching localized texts in the
+ * {@linkplain java.util.Locale#getDefault() system default locale}.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-1.2)
  * @version 0.3



Mime
View raw message