All Downloads are FREE. Search and download functionalities are using the official Maven repository.

base.text.LocalText Maven / Gradle / Ivy

/**
 * Creative commons Attribution-NonCommercial license.
 *
 * http://creativecommons.org/licenses/by-nc/2.5/au/deed.en_GB
 *
 * NO WARRANTY IS GIVEN OR IMPLIED, USE AT YOUR OWN RISK.
 */
package base.text;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.sql.DataSource;

/**
 * Cache all available translations of text strings discovered within the
 * application. Each newly discovered string may be persisted to the
 * database. Each string persisted in the database may have a corresponding
 * translation in any language.
 *
 * Initialise on web application startup as follows:
 *
 * 

 * LocalText.instance(dataSource);
 * 
* * At run time, text strings are looked up using the static helper function: * *

 * LocalText.local("Sample text", locale);
 * 
* * Request that any recently newly discovered text is persisted back into the * database: * *

 * LocalText.instance().persistNewTextForTranslation();
 * 
* * Translation helper functions
* The following methods are used to view and update text translations.

* * Search for a particular keyword in any string in any language: *

 * List items = LocalText.instance().search("keyword");
 * 
* * Search for a particular keyword in any string in a pre-defined language, or null for the default text: *

 * List items = LocalText.instance().search("en", "keyword");
 * 
* * Update the application with a new translation of a particular text string: * *

 * LocalText.instance().translate(hash, "en", "This is the english version of the string");
 * 
*/ public class LocalText { private static LocalText instance; public static LocalText instance(DataSource ds) throws IOException { if(instance == null) { instance = new LocalText(ds); } return instance; } public static String local(String text, String language) { if(instance == null) { throw new IllegalStateException("LocalText.instance() must be called before LocalText.local()"); } return instance.getTranslation(text, language); } public static String local(String text, Locale locale) { if(instance == null) { throw new IllegalStateException("LocalText.instance() must be called before LocalText.local()"); } return instance.getTranslation(text, locale.getLanguage() +"_" + locale.getCountry()); } private Map defaults = new Hashtable(); private Map> strings = new Hashtable>(); private Set databaseInsertQueue = new HashSet(); private DataSource ds; /** * Instantiate an instance of a LocalText cache, backed by a JDBC data source. * * @param ds * @throws IOException */ public LocalText(DataSource ds) throws IOException { this.ds = ds; loadTranslationsFromDatabase(); } /** * If a translation exists for the `text` parameter, return the translation, * otherwise return the `text` parameter itself. * * @param text Default text to display to the end user. * @param language Language to check for translated version of the text. * @return Returns the `text` input parameter unless a translation exists. */ public String getTranslation(String text, String language) { String result = null; Integer hash = text.hashCode(); if(strings.containsKey(language)) { result = strings.get(language).get(hash); } if(result == null) { result = text; if(!defaults.containsKey(hash)) { defaults.put(hash, text); registerNewTextForTranslation(text); } } return result; } public String getTranslation(String text, Locale locale) { return local(text, locale.getLanguage() +"_" + locale.getCountry()); } /** * Should be called on a regular scheduled interval by the web application, to ensure * any as yet untranslated text is added to the list of text strings. * @throws SQLException */ public void persistNewTextForTranslation() throws IOException { Connection c = null; PreparedStatement p1 = null; PreparedStatement p2 = null; ResultSet r = null; Set uncommitted = new HashSet(); try { c = ds.getConnection(); p2 = c.prepareStatement("select t_hash from translations where t_hash=? and t_lang='default'"); p1 = c.prepareStatement("insert into translations (t_hash,t_text,t_lang) values(?,?,'default')"); Set items = newTextForTranslation(); uncommitted.addAll(items); for(String text : items) { p2.setInt(1, text.hashCode()); r = p2.executeQuery(); if(!r.next()) { p1.setInt(1, text.hashCode()); p1.setString(2, text); p1.execute(); uncommitted.remove(text); } r.close(); r = null; } } catch(SQLException e) { throw new IOException(e); } finally { if(r != null) { try { r.close(); } catch(SQLException e) {} } if(p1 != null) { try { p1.close(); } catch(SQLException e) {} } if(p2 != null) { try { p2.close(); } catch(SQLException e) {} } if(c != null) { try { c.close(); } catch(SQLException e) {} } // If some sort of error results in saved text not being persisted, put // it back on the queue for later. for(String item : uncommitted) { registerNewTextForTranslation(item); } } } public void translate(int hash, String language, String newText) throws SQLException { if(language == null || language.equalsIgnoreCase("default")) { throw new IllegalArgumentException("May not directly define default text at this time."); } Connection c = null; PreparedStatement p1 = null; PreparedStatement p2 = null; PreparedStatement p3 = null; ResultSet r = null; try { c = ds.getConnection(); p3 = c.prepareStatement("select t_hash from translations where t_hash=? and t_lang='default'"); p3.setInt(1, hash); r = p3.executeQuery(); boolean hasDefault = true; if(!r.next()) { hasDefault = false; } r.close(); r = null; p3.close(); p3 = null; if(!hasDefault) { throw new IllegalArgumentException("Attempting to define a translation for a hash with no default text string."); } p2 = c.prepareStatement("select t_hash from translations where t_hash=? and t_lang=?"); p2.setInt(1, hash); p2.setString(2, language); r = p2.executeQuery(); if(!r.next()) { if(newText != null) { p1 = c.prepareStatement("insert into translations (t_hash,t_text,t_lang) values(?,?,?)"); p1.setInt(1, hash); p1.setString(2, newText); p1.setString(3, language); p1.execute(); } } else { if(newText == null) { p1 = c.prepareStatement("delete from translations where t_hash=? and t_lang=?"); p1.setInt(1, hash); p1.setString(2, language); p1.execute(); } else { p1 = c.prepareStatement("update translations set t_text=? where t_hash=? and t_lang=?"); p1.setString(1, newText); p1.setInt(2, hash); p1.setString(3, language); p1.execute(); } } r.close(); r = null; } finally { if(r != null) { r.close(); } if(p1 != null) { p1.close(); } if(p2 != null) { p2.close(); } if(p3 != null) { p3.close(); } if(c != null) { c.close(); } } if(!strings.containsKey(language)) { strings.put(language, new Hashtable()); } if(newText != null) { strings.get(language).put(hash, newText); } else { strings.get(language).remove(hash); } } /** * Called on web application startup to trigger loading all available translations * from the database. * * @throws IOException */ private void loadTranslationsFromDatabase() throws IOException { Connection c = null; PreparedStatement p = null; ResultSet r = null; try { c = ds.getConnection(); c.setAutoCommit(true); createTable(c); p = c.prepareStatement("select t_hash, t_text, t_lang from translations"); r = p.executeQuery(); while(r.next()) { int hash = r.getInt(1); String text = r.getString(2); String language = r.getString(3); if(text == null) { continue; } if(language.equalsIgnoreCase("default")) { defaults.put(hash, text); } else { if(!strings.containsKey(language)) { strings.put(language, new Hashtable()); } strings.get(language).put(hash, text); } } r.close(); r = null; } catch(SQLException e) { throw new IOException(e); } finally { if(r != null) { try { r.close(); } catch(SQLException e) {} } if(p != null) { try { p.close(); } catch(SQLException e) {} } if(c != null) { try { c.close(); } catch(SQLException e) {} } } } /** * * @throws IOException */ public List search(String keyword) throws IOException { Connection c = null; PreparedStatement p = null; ResultSet r = null; List results = new LinkedList(); try { c = ds.getConnection(); p = c.prepareStatement("select t_hash, t_text, t_lang from translations where t_text like lower(?) order by t_hash"); if(keyword == null || keyword.length() == 0 || keyword.equals("%") || keyword.equals("*")) { keyword = "%"; } else { if(!keyword.contains("%")) { keyword = "%" + keyword.toLowerCase() + "%"; } else { keyword = keyword.toLowerCase(); } } p.setString(1, keyword); r = p.executeQuery(); while(r.next()) { results.add(new LocalTranslation(r.getInt(1), r.getString(3), r.getString(2))); } } catch(SQLException e) { throw new IOException(e); } finally { if(r != null) { try { r.close(); } catch(SQLException e) {} } if(p != null) { try { p.close(); } catch(SQLException e) {} } if(c != null) { try { c.close(); } catch(SQLException e) {} } } return results; } /** * * @throws IOException */ public Collection export() throws IOException { Connection c = null; PreparedStatement p = null; ResultSet r = null; Map results = new Hashtable(); try { c = ds.getConnection(); p = c.prepareStatement( "select t_hash, t_text, t_lang from translations where t_lang='default' " + "union " + "select t_hash, t_text, t_lang from translations where t_lang!='default' " ); r = p.executeQuery(); while(r.next()) { if(r.getString(3).equals("default")) { results.put(r.getInt(1), new LocalTranslationSet(r.getInt(1), r.getString(2))); } else { LocalTranslationSet i = results.get(r.getInt(1)); if(i == null) { System.err.println("Missing default text for string: " + r.getString(2) + ":" + r.getString(3)); } else { i.addText(r.getString(3), r.getString(2)); } } } } catch(SQLException e) { throw new IOException(e); } finally { if(r != null) { try { r.close(); } catch(SQLException e) {} } if(p != null) { try { p.close(); } catch(SQLException e) {} } if(c != null) { try { c.close(); } catch(SQLException e) {} } } return results.values(); } /** * * @throws IOException */ public List search(int hash) throws IOException { Connection c = null; PreparedStatement p = null; ResultSet r = null; List results = new LinkedList(); try { c = ds.getConnection(); p = c.prepareStatement("select t_hash, t_text, t_lang from translations where t_hash=? order by t_lang"); p.setInt(1, hash); r = p.executeQuery(); while(r.next()) { results.add(new LocalTranslation(r.getInt(1), r.getString(3), r.getString(2))); } } catch(SQLException e) { throw new IOException(e); } finally { if(r != null) { try { r.close(); } catch(SQLException e) {} } if(p != null) { try { p.close(); } catch(SQLException e) {} } if(c != null) { try { c.close(); } catch(SQLException e) {} } } return results; } /** * Wipe all information about translations from memory and from the database. Used by * the test cases to reset test data. * * @throws SQLException */ public void wipe() throws SQLException { Connection c = null; PreparedStatement p = null; try { c = ds.getConnection(); c.setAutoCommit(true); createTable(c); p = c.prepareStatement("delete from translations"); p.executeUpdate(); defaults = new Hashtable<>(); strings = new Hashtable<>(); databaseInsertQueue = new HashSet<>(); } finally { if(p != null) { p.close(); } if(c != null) { c.close(); } } } /** * Add new strings that require translation. * * @param text */ private synchronized void registerNewTextForTranslation(String text) { databaseInsertQueue.add(text); } /** * Retrieve the list of recently discovered strings that require translation. * * @return List of recently discovered strings that need translation. */ private synchronized Set newTextForTranslation() { Setitems = databaseInsertQueue; databaseInsertQueue = new HashSet<>(); return items; } /** * Create the translations database table. Rather than trying to guess which * database type we are talking to, just issue create statements for both MySQL * and/or Oracle to see which one sticks. * * @param c */ private void createTable(Connection c) { PreparedStatement s = null; try { s = c.prepareStatement( "create table translations (" + " t_hash integer, "+ " t_lang nvarchar2(32), " + " t_text nvarchar2(254), " + " CONSTRAINT translations_pk PRIMARY KEY (t_hash,t_lang)) "); s.execute(); } catch(SQLException e) { // Fails if table already exists, or this is not Oracle. } finally { if(s != null) { try { s.close(); s = null; } catch(Exception f){} } } try { s = c.prepareStatement( "create table if not exists translations (" + " t_hash integer, " + " t_lang varchar(32), "+ " t_text varchar(254), "+ " PRIMARY KEY(t_hash,t_lang)) ENGINE=InnoDB DEFAULT CHARSET=utf8"); s.execute(); } catch(SQLException e) { // Fails if this is not MySQL } finally { if(s != null) { try { s.close(); } catch(Exception f){} } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy