commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Mattias Jiderhamn <mj-li...@expertsystem.se>
Subject [sandbox/i18n] Could sombody commit my JDBC provider?
Date Wed, 13 Jul 2005 20:35:35 GMT
I haven't heard from Daniel Florey for a while 
now, so my guess is he is on vacation. Therefore 
I wonder if there is somebody else with sandbox 
commit access that could commit my patch below, 
adding a JDBC/database MessageProvider to the i18n component.

I intend to continue working on the remaining 
issues (as of e-mail on 2005-06-18) and I would 
prefer to separate the different issues into 
different patches/commits, rather than one big "my contributions".

If needed for correct encoding/line breaks, I 
could e-mail a zipped diff to somebody privately, or create a Bugzilla entry.

Thanks in advance.
   Mattias Jiderhamn

Here is the patch:
--------------------------------------------------------

Index: project.properties
===================================================================
--- project.properties	(revision 201952)
+++ project.properties	(working copy)
@@ -28,3 +28,8 @@
  # M A V E N  J A R  O V E R R I D E
  # ------------------------------------------------------------------------
  maven.jar.override = on
+
+
+# Set target to Java 1.4, since JCoverage does not work with Java 1.5
+maven.compile.source=1.4
+maven.compile.target=1.4
\ No newline at end of file
Index: project.xml
===================================================================
--- project.xml	(revision 201952)
+++ project.xml	(working copy)
@@ -74,6 +74,23 @@
      </contributors>

      <dependencies>
+        <!-- Please note, these depenencies are 
needed for unit tests only! -->
+        <dependency>
+            <id>hsqldb</id>
+            <version>1.7.3.3</version>
+        </dependency>
+        <dependency>
+            <id>commons-dbcp</id>
+            <version>1.2.1</version>
+        </dependency>
+        <dependency>
+            <id>commons-pool</id>
+            <version>1.2</version>
+        </dependency>
+        <dependency>
+            <id>commons-collections</id>
+            <version>2.1.1</version>
+        </dependency>
      </dependencies>

      <build>
Index: src/test/org/apache/commons/i18n/I18nUtilsTest.java
===================================================================
--- src/test/org/apache/commons/i18n/I18nUtilsTest.java	(revision 0)
+++ src/test/org/apache/commons/i18n/I18nUtilsTest.java	(revision 0)
@@ -0,0 +1,45 @@
+/*
+*
+* ====================================================================
+*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed 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.commons.i18n;
+
+import junit.framework.TestCase;
+
+import java.util.Locale;
+
+/**
+ * @author Mattias Jiderhamn
+ */
+public class I18nUtilsTest extends TestCase {
+    public void testGetParentLocale() {
+        assertEquals("Language, country and variant",
+                new Locale("en", "GB"),
+                I18nUtils.getParentLocale(new 
Locale("en", "GB", "scottish")));
+
+        assertEquals("Language and country",
+                Locale.ENGLISH,
+                I18nUtils.getParentLocale(new Locale("en", "GB")));
+
+        assertEquals("Language and variant",
+                Locale.ENGLISH,
+                I18nUtils.getParentLocale(new Locale("en", "", "scottish")));
+
+        assertNull("Language only", 
I18nUtils.getParentLocale(Locale.ENGLISH));
+    }
+}
Index: src/test/org/apache/commons/i18n/ResourceBundleMessageProviderTest.java
===================================================================
--- 
src/test/org/apache/commons/i18n/ResourceBundleMessageProviderTest.java 
(revision 201952)
+++ 
src/test/org/apache/commons/i18n/ResourceBundleMessageProviderTest.java 
(working copy)
@@ -137,7 +137,7 @@
          Map germanEntries = new 
ResourceBundleMessageProvider("messageBundle").getEntries("helloWorld", 
Locale.GERMAN);
          assertEquals("No of entries", 3, germanEntries.size());
          assertEquals("Hallo Welt", germanEntries.get("title"));
-        assertEquals("Ich w�nsche Dir alles 
Gute und ein frohes Fest!", germanEntries.get("text"));
+        assertEquals("Ich wünsche Dir alles Gute 
und ein frohes Fest!", germanEntries.get("text"));
          assertEquals("This entry is not 
translated to any other languages", germanEntries.get("notTranslated"));

          Map frenchEntries = new 
ResourceBundleMessageProvider("messageBundle").getEntries("helloWorld", 
Locale.FRENCH);
Index: src/test/org/apache/commons/i18n/JdbcMessageProviderTest.java
===================================================================
--- src/test/org/apache/commons/i18n/JdbcMessageProviderTest.java	(revision 0)
+++ src/test/org/apache/commons/i18n/JdbcMessageProviderTest.java	(revision 0)
@@ -0,0 +1,169 @@
+package org.apache.commons.i18n;
+
+import junit.framework.TestCase;
+
+import java.sql.DriverManager;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.dbcp.BasicDataSource;
+
+/**
+ * @author Mattias Jiderhamn
+ */
+public class JdbcMessageProviderTest extends TestCase {
+
+    private static Connection getNewConnection() throws SQLException {
+        return 
DriverManager.getConnection("jdbc:hsqldb:.", 
"sa", ""); // Connect to in-memory database
+    }
+
+    public void setUp() throws Exception {
+        /* Make sure en_US is the default Locale for tests */
+        Locale.setDefault(Locale.US);
+
+        Class.forName("org.hsqldb.jdbcDriver"); // Load HSQLDB database driver
+        Connection conn = getNewConnection();
+        Statement stmt = conn.createStatement();
+        stmt.execute(
+                "CREATE TABLE messages ( " +
+                "  'id' VARCHAR(30), " +
+                "  'language' VARCHAR(2), " +
+                "  'title' VARCHAR(100), " +
+                "  'text' VARCHAR(100)" +
+                ")");
+        stmt.execute(
+                "INSERT INTO messages VALUES (" +
+                "  'helloWorld', 'en', " +
+                "  'Hello World', 'I wish you a merry christmas!'" +
+                ")"
+        );
+        stmt.execute(
+                "INSERT INTO messages VALUES (" +
+                "  'helloWorld', 'de', " +
+                "  'Hallo Welt', 'Ich wünsche 
Dir alles Gute und ein frohes Fest!'" +
+                ")"
+        );
+        stmt.close();
+        conn.close();
+    }
+
+    public void tearDown() throws Exception {
+        Connection conn = getNewConnection();
+        conn.createStatement().execute(
+                "DROP TABLE messages"
+        );
+        conn.close();
+    }
+
+    public void testConstructors() throws Exception {
+        // Connection constructor
+        Connection conn = 
DriverManager.getConnection("jdbc:hsqldb:.", 
"sa", ""); // Connect to in-memory database
+        JdbcMessageProvider jdbcMessageProvider 
= new JdbcMessageProvider(conn, "messages", "id", "language");
+        conn.close();
+        assertEquals("Hello World", 
jdbcMessageProvider.getText("helloWorld", "title", Locale.ENGLISH));
+
+        // DataSource constructor
+        BasicDataSource dataSource = new BasicDataSource();
+        dataSource.setUrl("jdbc:hsqldb:.");
+        dataSource.setUsername("sa");
+        dataSource.setPassword("");
+        jdbcMessageProvider = new 
JdbcMessageProvider(dataSource, "messages", "id", "language");
+        assertEquals("Hello World", 
jdbcMessageProvider.getText("helloWorld", "title", Locale.ENGLISH));
+
+        // Map/Properties constructor
+        Properties props = new Properties();
+        props.setProperty("jdbc.connect.driver", "org.hsqldb.jdbcDriver");
+        props.setProperty("jdbc.connect.url", "jdbc:hsqldb:.");
+        props.setProperty("jdbc.connect.login", "sa");
+        props.setProperty("jdbc.connect.password", "");
+
+        props.setProperty("jdbc.sql.table", "messages");
+        props.setProperty("jdbc.sql.key.column", "id");
+        props.setProperty("jdbc.sql.locale.column", "language");
+        jdbcMessageProvider = new JdbcMessageProvider(props);
+        assertEquals("Hello World", 
jdbcMessageProvider.getText("helloWorld", "title", Locale.ENGLISH));
+
+        // Test install
+        MessageManager.addMessageProvider("messages", jdbcMessageProvider);
+        assertEquals("Hello World", 
MessageManager.getText("helloWorld", "title", null, Locale.ENGLISH));
+    }
+
+    public void testGetText() throws Exception {
+        Connection conn = 
DriverManager.getConnection("jdbc:hsqldb:.", 
"sa", ""); // Connect to in-memory database
+        JdbcMessageProvider jdbcMessageProvider 
= new JdbcMessageProvider(conn, "messages", "id", "language");
+        conn.close();
+
+        // Explicit default locale
+        assertEquals("Hello World", 
jdbcMessageProvider.getText("helloWorld", "title", Locale.ENGLISH));
+        assertEquals("I wish you a merry 
christmas!", 
jdbcMessageProvider.getText("helloWorld", "text", Locale.ENGLISH));
+
+        // Default locale with country
+        assertEquals("Hello World", 
jdbcMessageProvider.getText("helloWorld", "title", Locale.US));
+        assertEquals("I wish you a merry 
christmas!", jdbcMessageProvider.getText("helloWorld", "text", Locale.US));
+
+        // Default locale with country and variant
+        Locale scottish = new Locale("en", "", "scottish");
+        assertEquals("Hello World", 
jdbcMessageProvider.getText("helloWorld", "title", scottish));
+        assertEquals("I wish you a merry 
christmas!", jdbcMessageProvider.getText("helloWorld", "text", scottish));
+
+        assertEquals("Hallo Welt", 
jdbcMessageProvider.getText("helloWorld", "title", Locale.GERMAN));
+        assertEquals("Ich wünsche Dir alles Gute 
und ein frohes Fest!", 
jdbcMessageProvider.getText("helloWorld", "text", Locale.GERMAN));
+
+        // Default locale with country
+        assertEquals("Hallo Welt", 
jdbcMessageProvider.getText("helloWorld", "title", Locale.GERMANY));
+        assertEquals("Ich wünsche Dir alles Gute 
und ein frohes Fest!", 
jdbcMessageProvider.getText("helloWorld", "text", Locale.GERMANY));
+
+        // Test use of defaule
+        assertEquals("Hello World", 
jdbcMessageProvider.getText("helloWorld", "title", Locale.JAPANESE));
+        assertEquals("I wish you a merry 
christmas!", 
jdbcMessageProvider.getText("helloWorld", "text", Locale.JAPANESE));
+
+        // Test non-existent
+        assertNull(jdbcMessageProvider.getText("foo", "bar", Locale.ENGLISH));
+    }
+
+    public void testGetEntries() throws Exception {
+        Connection conn = 
DriverManager.getConnection("jdbc:hsqldb:.", 
"sa", ""); // Connect to in-memory database
+        JdbcMessageProvider jdbcMessageProvider 
= new JdbcMessageProvider(conn, "messages", "id", "language");
+        conn.close();
+
+        // Explicit default locale
+        Map entries = 
jdbcMessageProvider.getEntries("helloWorld", Locale.ENGLISH);
+        assertEquals("No of entries", 2, entries.size());
+        assertEquals("Hello World", (String)entries.get("title"));
+        assertEquals("I wish you a merry 
christmas!", (String)entries.get("text"));
+
+        // Default locale with country
+        entries = jdbcMessageProvider.getEntries("helloWorld", Locale.US);
+        assertEquals("No of entries", 2, entries.size());
+        assertEquals("Hello World", (String)entries.get("title"));
+        assertEquals("I wish you a merry 
christmas!", (String)entries.get("text"));
+
+        // Default locale with country and variant
+        Locale scottish = new Locale("en", "", "scottish");
+        entries = jdbcMessageProvider.getEntries("helloWorld", scottish);
+        assertEquals("No of entries", 2, entries.size());
+        assertEquals("Hello World", (String)entries.get("title"));
+        assertEquals("I wish you a merry 
christmas!", (String)entries.get("text"));
+
+        entries = jdbcMessageProvider.getEntries("helloWorld", Locale.GERMAN);
+        assertEquals("No of entries", 2, entries.size());
+        assertEquals("Hallo Welt", (String)entries.get("title"));
+        assertEquals("Ich wünsche Dir alles Gute 
und ein frohes Fest!", (String)entries.get("text"));
+
+        // Default locale with country
+        entries = 
jdbcMessageProvider.getEntries("helloWorld", Locale.GERMANY);
+        assertEquals("No of entries", 2, entries.size());
+        assertEquals("Hallo Welt", (String)entries.get("title"));
+        assertEquals("Ich wünsche Dir alles Gute 
und ein frohes Fest!", (String)entries.get("text"));
+
+        // Test use of defaule
+        entries = 
jdbcMessageProvider.getEntries("helloWorld", Locale.JAPANESE);
+        assertEquals("No of entries", 2, entries.size());
+        assertEquals("Hello World", (String)entries.get("title"));
+        assertEquals("I wish you a merry 
christmas!", (String)entries.get("text"));
+    }
+}
Index: src/java/org/apache/commons/i18n/XMLMessageProvider.java
===================================================================
--- src/java/org/apache/commons/i18n/XMLMessageProvider.java	(revision 201952)
+++ src/java/org/apache/commons/i18n/XMLMessageProvider.java	(working copy)
@@ -123,9 +123,11 @@
      private Message lookupMessage(String id, Locale locale) {
          String key = id + '_' + locale.toString();
          if (messages.containsKey(key)) return (Message)messages.get(key);
-        while (key.lastIndexOf('_') > 0) {
-            key = key.substring(0, key.lastIndexOf('_'));
+        locale = I18nUtils.getParentLocale(locale);
+        while (locale != null) {
+            key = id + '_' + locale.toString();
              if (messages.containsKey(key)) return (Message)messages.get(key);
+            locale = I18nUtils.getParentLocale(locale);
          }
          return null;
      }
Index: src/java/org/apache/commons/i18n/I18nUtils.java
===================================================================
--- src/java/org/apache/commons/i18n/I18nUtils.java	(revision 0)
+++ src/java/org/apache/commons/i18n/I18nUtils.java	(revision 0)
@@ -0,0 +1,40 @@
+/*
+*
+* ====================================================================
+*
+* Copyright 2004 The Apache Software Foundation
+*
+* Licensed 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.commons.i18n;
+
+import java.util.Locale;
+
+/**
+ * This class holds utility methods useful when working with i18n.
+ * @author Mattias Jiderhamn
+ */
+public class I18nUtils {
+    private I18nUtils() {
+    }
+
+    public static Locale getParentLocale (Locale locale) {
+        if(locale.getVariant().length() != 0)
+          return new Locale(locale.getLanguage(), locale.getCountry());
+        else if(locale.getCountry().length() != 0)
+            return new Locale(locale.getLanguage());
+        else // Locale with only language have no parent
+            return null;
+    }
+}
\ No newline at end of file
Index: src/java/org/apache/commons/i18n/JdbcMessageProvider.java
===================================================================
--- src/java/org/apache/commons/i18n/JdbcMessageProvider.java	(revision 0)
+++ src/java/org/apache/commons/i18n/JdbcMessageProvider.java	(revision 0)
@@ -0,0 +1,183 @@
+package org.apache.commons.i18n;
+
+import javax.sql.DataSource;
+import java.util.*;
+import java.sql.*;
+
+/**
+ * @author Mattias Jiderhamn
+ */
+public class JdbcMessageProvider implements MessageProvider {
+    /**
+     * This Map has locale or language as key, and a Map with the different
+     * messages as value.
+     */
+    private final Map locales = new HashMap();
+
+    private String idColumn;
+
+    private String languageColumn;
+
+    public JdbcMessageProvider(Connection conn, 
String table, String idColumn, String languageColumn)
+            throws SQLException {
+        this.idColumn = idColumn;
+        this.languageColumn = languageColumn;
+        init(conn, table);
+    }
+
+    public JdbcMessageProvider(DataSource ds, 
String table, String idColumn, String languageColumn)
+            throws SQLException {
+        this.idColumn = idColumn;
+        this.languageColumn = languageColumn;
+        Connection conn = null;
+        try {
+            conn = ds.getConnection();
+            init(conn, table);
+        }
+        finally {
+            if(conn != null)
+                conn.close();
+        }
+    }
+
+    /**
+     * Create JDBC MessageProvider from properties in a Map, such
+     * as a java.util.Properties object. The 
following are the properties in use, which
+     * are the same as for JDBCResources of Jakarta Commons Resources
+     * jdbc.connect.driver               = org.gjt.mm.mysql.Driver
+     * jdbc.connect.url                  = jdbc:mysql://localhost/resources
+     * jdbc.connect.login                = resourcesTest
+     * jdbc.connect.password             = resourcesTest
+     *
+     * jdbc.sql.table                    = resources
+     * jdbc.sql.locale.column            = locale
+     * jdbc.sql.key.column               = msgKey
+     */
+    public JdbcMessageProvider(Map properties) 
throws ClassNotFoundException, SQLException {
+        String driver = (String)properties.get("jdbc.connect.driver");
+        String url    = (String)properties.get("jdbc.connect.url");
+        String user = (String)properties.get("jdbc.connect.login");
+        String pass = (String)properties.get("jdbc.connect.password");
+
+        String table = (String)properties.get("jdbc.sql.table");
+        this.idColumn = (String)properties.get("jdbc.sql.key.column");
+        this.languageColumn = 
(String)properties.get("jdbc.sql.locale.column");
+
+        Class.forName(driver);
+        Connection conn = null;
+        try {
+            conn = DriverManager.getConnection(url, user, pass);
+            init(conn, table);
+        }
+        finally {
+            if(conn != null)
+                conn.close();
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////
+    // Methods for initialization
+    ///////////////////////////////////////////////////////////////////////
+
+    private void init(Connection conn, String table) throws SQLException {
+        Statement stmt = null;
+        ResultSet rs = null;
+        try {
+            stmt = conn.createStatement();
+            rs = stmt.executeQuery("SELECT * FROM " + table);
+            String[] valueColumns = getValueColumns(rs);
+            while(rs.next()) {
+                String id = rs.getString(idColumn);
+                Locale locale = getLocale(rs);
+                Map entries = new HashMap();
+                for(int i = 0; i < valueColumns.length; i++) {
+                    entries.put(valueColumns[i], 
rs.getString(valueColumns[i]));
+                }
+                Map localeMap = (Map)locales.get(locale);
+                if(localeMap == null) { // If first record for this Locale
+                    localeMap = new HashMap();
+                    locales.put(locale, localeMap);
+                }
+                localeMap.put(id, entries);
+            }
+        }
+        finally {
+            if(stmt != null)
+                stmt.close();
+            if(rs != null)
+              rs.close();
+        }
+    }
+
+    /**
+     * Get a String of all the column names, except the ID column and the
+     * language column.
+     * @param rs A <code>ResultSet</code> ready for reading meta data.
+     * @return A String array with the text value column names.
+     * @throws SQLException If an SQL error occurs.
+     */
+    protected String[] getValueColumns(ResultSet rs) throws SQLException {
+        List output = new LinkedList();
+        ResultSetMetaData metadata = rs.getMetaData();
+        int count = metadata.getColumnCount();
+        for(int i = 0; i < count; i++) {
+            String columnName = metadata.getColumnName(i+1); // (Count from 1)
+            if(! idColumn.equals(columnName) && 
! languageColumn.equals(columnName) )
+                output.add(columnName);
+        }
+        return (String[])output.toArray(new String[0]);
+    }
+
+    /**
+     * Get <code>Locale</code> for the current 
record in the ResultSet. May be overridden
+     * by subclasses to allow for proprietary interpretation of language data.
+     * The default implementation assumes the 
column with the name provided as languageColumn
+     * for the constructor contains the ISO-639 code.
+     * @return The <code>Locale</code> of the 
current <code>ResultSet</code> record.
+     */
+    protected Locale getLocale(ResultSet rs) throws SQLException {
+        return new Locale(rs.getString(languageColumn));
+    }
+
+    ///////////////////////////////////////////////////////////////////////
+    // Methods to implement MessageProvider
+    ///////////////////////////////////////////////////////////////////////
+
+    public String getText(String id, String 
entry, Locale locale) throws MessageNotFoundException {
+        // TODO: Add Logging
+        Map entries = getEntries(id, locale);
+        if(entries != null) {
+            // TODO: Consider whether we need to 
recurse up if entries does not contain requested entry
+            return (String)entries.get(entry);
+        }
+        else
+            return null;
+    }
+
+    public Map getEntries(String id, Locale 
locale) throws MessageNotFoundException {
+        Map entries = findEntriesRecursively(id,locale);
+        if(entries == null) // If not found by 
using specified locale, try to use default
+            entries = findEntriesRecursively(id,Locale.getDefault());
+        return entries;
+    }
+
+    /**
+     * Find entries by looking at the parent 
locale (language, country, variant ->
+     * language, country -> language) until 
entry is found. If entry not found for topmost
+     * Locale (language only), null is returned.
+     */
+    private Map findEntriesRecursively(String id, Locale locale) {
+        Map localeIds = (Map)locales.get(locale);
+        if(localeIds != null) {
+            Map entries = (Map)localeIds.get(id);
+            if(entries != null)
+              return entries;
+        }
+        Locale parentLocale = I18nUtils.getParentLocale(locale);
+        if(parentLocale == null)
+            return null;
+        else
+            return findEntriesRecursively(id, parentLocale); // Recursive call
+    }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Mime
View raw message