Project: * Clean-up (Eclipse cruft, unused files, etc...) * Git-specific changes * Maven POMs clean-up and changes for the build system * Version set to 1.0.0-0 in the development branches * Maven plug-ins updated to latest versions * Very partial dev. documentation added
This commit is contained in:
parent
c74e30d5ba
commit
0665a760de
1439 changed files with 1020 additions and 1649 deletions
legacyworlds-server-beans-i18n
pom.xml
src
main
java/com/deepclone/lw/beans/i18n
I18NAdministrationImpl.javaI18NData.javaI18NManagerBean.javaLanguageStore.javaLoaderTransaction.javaTranslatorBean.java
resources/configuration
test
16
legacyworlds-server-beans-i18n/pom.xml
Normal file
16
legacyworlds-server-beans-i18n/pom.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>legacyworlds-server-beans</artifactId>
|
||||
<groupId>com.deepclone.lw</groupId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../legacyworlds-server-beans/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>legacyworlds-server-beans-i18n</artifactId>
|
||||
<name>Legacy Worlds - Server - Components - Internationalisation</name>
|
||||
<description>This package defines the components which control server-side internationalised text management.</description>
|
||||
<version>${legacyworlds.version.main}.${legacyworlds.version.release}-${legacyworlds.version.build}</version>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,232 @@
|
|||
package com.deepclone.lw.beans.i18n;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.deepclone.lw.interfaces.i18n.*;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The implementation of the I18N administrative session uses the manager's shared {@link I18NData}
|
||||
* instance to modify the I18N database, logging all actions in the process. It also provides some
|
||||
* read access to languages and translations, but (as opposed to access through the
|
||||
* {@link Translator} bean) completely ignores language support requirements, allowing new or
|
||||
* incomplete languages to be managed.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
class I18NAdministrationImpl
|
||||
implements I18NAdministration
|
||||
{
|
||||
|
||||
/** The shared I18N data instance */
|
||||
private I18NData data;
|
||||
private int administrator;
|
||||
|
||||
|
||||
/**
|
||||
* Copies the various required references
|
||||
*
|
||||
* @param data
|
||||
* shared I18N data instance
|
||||
* @param administrator
|
||||
*/
|
||||
I18NAdministrationImpl( I18NData data, int administrator )
|
||||
{
|
||||
this.data = data;
|
||||
this.administrator = administrator;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in I18NAdministration interface */
|
||||
@Override
|
||||
public Set< String > getLanguages( )
|
||||
{
|
||||
this.data.readLock( ).lock( );
|
||||
try {
|
||||
return this.data.getLanguages( );
|
||||
} finally {
|
||||
this.data.readLock( ).unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Documented in I18NAdministration interface */
|
||||
@Override
|
||||
public String getLanguageName( String language )
|
||||
throws UnknownLanguageException
|
||||
{
|
||||
String l;
|
||||
this.data.readLock( ).lock( );
|
||||
try {
|
||||
l = this.data.getLanguageName( language );
|
||||
} finally {
|
||||
this.data.readLock( ).unlock( );
|
||||
}
|
||||
|
||||
if ( l == null ) {
|
||||
throw new UnknownLanguageException( language );
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in I18NAdministration interface */
|
||||
@Override
|
||||
public double getLanguageSupport( String language )
|
||||
throws UnknownLanguageException
|
||||
{
|
||||
int sCount;
|
||||
double lSize;
|
||||
|
||||
this.data.readLock( ).lock( );
|
||||
try {
|
||||
if ( !this.data.hasLanguage( language ) ) {
|
||||
throw new UnknownLanguageException( language );
|
||||
}
|
||||
lSize = this.data.getLanguageSize( language );
|
||||
sCount = this.data.getStringsCount( );
|
||||
} finally {
|
||||
this.data.readLock( ).unlock( );
|
||||
}
|
||||
|
||||
if ( sCount == 0 ) {
|
||||
return 0;
|
||||
}
|
||||
return lSize / (double) sCount;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in I18NAdministration interface */
|
||||
@Override
|
||||
public void createLanguage( String language , String name )
|
||||
throws DuplicateLanguageException
|
||||
{
|
||||
this.data.writeLock( ).lock( );
|
||||
try {
|
||||
if ( !this.data.addLanguage( this.administrator , language , name ) ) {
|
||||
throw new DuplicateLanguageException( language );
|
||||
}
|
||||
} finally {
|
||||
this.data.writeLock( ).unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Documented in I18NAdministration interface */
|
||||
@Override
|
||||
public void setLanguageName( String language , String name )
|
||||
throws UnknownLanguageException
|
||||
{
|
||||
String oldName;
|
||||
|
||||
this.data.writeLock( ).lock( );
|
||||
try {
|
||||
oldName = this.data.updateLanguage( this.administrator , language , name );
|
||||
if ( oldName == null ) {
|
||||
throw new UnknownLanguageException( language );
|
||||
}
|
||||
} finally {
|
||||
this.data.writeLock( ).unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set< String > getStrings( )
|
||||
{
|
||||
this.data.readLock( ).lock( );
|
||||
try {
|
||||
return this.data.getStrings( );
|
||||
} finally {
|
||||
this.data.readLock( ).unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Documented in I18NAdministration interface */
|
||||
@Override
|
||||
public String getTranslation( String language , String string )
|
||||
throws UnknownStringException , UnknownLanguageException
|
||||
{
|
||||
this.data.readLock( ).lock( );
|
||||
try {
|
||||
if ( !this.data.hasLanguage( language ) ) {
|
||||
throw new UnknownLanguageException( language );
|
||||
}
|
||||
if ( !this.data.hasString( string ) ) {
|
||||
throw new UnknownStringException( string );
|
||||
}
|
||||
return this.data.getTranslation( language , string );
|
||||
} finally {
|
||||
this.data.readLock( ).unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean updateTranslation( String language , String string , String translation )
|
||||
throws UnknownStringException , UnknownLanguageException
|
||||
{
|
||||
String oldString;
|
||||
this.data.writeLock( ).lock( );
|
||||
try {
|
||||
if ( !this.data.hasLanguage( language ) ) {
|
||||
throw new UnknownLanguageException( language );
|
||||
}
|
||||
if ( !this.data.hasString( string ) ) {
|
||||
throw new UnknownStringException( string );
|
||||
}
|
||||
oldString = this.data.setTranslation( this.administrator , language , string , translation );
|
||||
} finally {
|
||||
this.data.writeLock( ).unlock( );
|
||||
}
|
||||
return ( oldString == null );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in I18NAdministration interface */
|
||||
@Override
|
||||
public void createString( String string , Map< String , String > translations )
|
||||
throws DuplicateStringException , UnknownLanguageException , InvalidUpdateException
|
||||
{
|
||||
Set< String > languages;
|
||||
|
||||
this.data.writeLock( ).lock( );
|
||||
try {
|
||||
// Check for duplicate string ID
|
||||
if ( this.data.hasString( string ) ) {
|
||||
throw new DuplicateStringException( string );
|
||||
}
|
||||
|
||||
// Make sure all specified languages actually exist
|
||||
languages = this.data.getLanguages( );
|
||||
for ( String lId : translations.keySet( ) ) {
|
||||
if ( !languages.contains( lId ) ) {
|
||||
throw new UnknownLanguageException( lId );
|
||||
}
|
||||
languages.remove( lId );
|
||||
}
|
||||
|
||||
// Make sure no 100% supported language becomes unsupported because a translation is
|
||||
// missing
|
||||
int sCount = this.data.getStringsCount( );
|
||||
for ( String lId : languages ) {
|
||||
if ( this.data.getLanguageSize( lId ) < sCount ) {
|
||||
languages.remove( lId );
|
||||
}
|
||||
}
|
||||
if ( !languages.isEmpty( ) ) {
|
||||
throw new InvalidUpdateException( languages );
|
||||
}
|
||||
|
||||
// Create the string
|
||||
this.data.createString( this.administrator , string , translations );
|
||||
} finally {
|
||||
this.data.writeLock( ).unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,350 @@
|
|||
package com.deepclone.lw.beans.i18n;
|
||||
|
||||
|
||||
import java.sql.Types;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import com.deepclone.lw.utils.StoredProc;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This class is used by all parts of the I18N support system; it centralises all I18N-related data,
|
||||
* providing both read and write access. It implements the {@link ReadWriteLock} interface, although
|
||||
* only as a convenience.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
class I18NData
|
||||
implements ReadWriteLock
|
||||
{
|
||||
/** The instance's read/write lock */
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock( );
|
||||
|
||||
/** Database interface */
|
||||
private final SimpleJdbcTemplate dTemplate;
|
||||
|
||||
/** Transaction manager interface */
|
||||
private final TransactionTemplate tTemplate;
|
||||
|
||||
/** String definitions, by identifier */
|
||||
private HashSet< String > strings;
|
||||
|
||||
/** Languages by identifier. Each language is represented by a {@link LanguageStore} */
|
||||
private HashMap< String , LanguageStore > languages;
|
||||
|
||||
private StoredProc fUocLanguage;
|
||||
private StoredProc fUocTranslation;
|
||||
|
||||
|
||||
/** Copies the required references then loads all data */
|
||||
I18NData( DataSource dataSource , TransactionTemplate tTemplate )
|
||||
{
|
||||
this.dTemplate = new SimpleJdbcTemplate( dataSource );
|
||||
this.tTemplate = tTemplate;
|
||||
|
||||
this.fUocLanguage = new StoredProc( dataSource , "defs" , "uoc_language" );
|
||||
this.fUocLanguage.addParameter( "language" , Types.VARCHAR );
|
||||
this.fUocLanguage.addParameter( "name" , Types.VARCHAR );
|
||||
this.fUocLanguage.addParameter( "admin_id" , Types.INTEGER );
|
||||
|
||||
this.fUocTranslation = new StoredProc( dataSource , "defs" , "uoc_translation" );
|
||||
this.fUocTranslation.addParameter( "language" , Types.VARCHAR );
|
||||
this.fUocTranslation.addParameter( "string_id" , Types.VARCHAR );
|
||||
this.fUocTranslation.addParameter( "contents" , Types.VARCHAR );
|
||||
this.fUocTranslation.addParameter( "admin_id" , Types.INTEGER );
|
||||
|
||||
this.loadAll( );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Re)initialises the strings and languages definition maps, then starts a loader transaction
|
||||
* to fill them from the database's contents.
|
||||
*/
|
||||
void loadAll( )
|
||||
{
|
||||
this.strings = new HashSet< String >( );
|
||||
this.languages = new HashMap< String , LanguageStore >( );
|
||||
|
||||
LoaderTransaction trans = new LoaderTransaction( this.dTemplate , this.strings , this.languages );
|
||||
this.tTemplate.execute( trans );
|
||||
}
|
||||
|
||||
|
||||
/** @return the set of known language identifiers */
|
||||
Set< String > getLanguages( )
|
||||
{
|
||||
return new HashSet< String >( this.languages.keySet( ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param identifier
|
||||
* a language's identifier
|
||||
* @return whether a language exists or not
|
||||
*/
|
||||
boolean hasLanguage( String identifier )
|
||||
{
|
||||
return this.languages.containsKey( identifier );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* a string identifier
|
||||
* @return whether such a string definition exists or not
|
||||
*/
|
||||
boolean hasString( String string )
|
||||
{
|
||||
return this.strings.contains( string );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param identifier
|
||||
* a language's identifier
|
||||
* @return the amount of translations defined for the specified language, or 0 if the language
|
||||
* does not exist
|
||||
*/
|
||||
int getLanguageSize( String identifier )
|
||||
{
|
||||
if ( !this.hasLanguage( identifier ) ) {
|
||||
return 0;
|
||||
}
|
||||
return this.languages.get( identifier ).getTranslationsCount( );
|
||||
}
|
||||
|
||||
|
||||
/** @return the amount of defined string identifiers */
|
||||
int getStringsCount( )
|
||||
{
|
||||
return this.strings.size( );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether a language is fully supported.
|
||||
*
|
||||
* @param identifier
|
||||
* the language's identifier
|
||||
* @return true if there's a translation for each string definition in the specified language.
|
||||
*/
|
||||
boolean isLanguageComplete( String identifier )
|
||||
{
|
||||
return this.getLanguageSize( identifier ) == this.getStringsCount( );
|
||||
}
|
||||
|
||||
|
||||
/** @return the set of defined string identifiers */
|
||||
Set< String > getStrings( )
|
||||
{
|
||||
return new HashSet< String >( this.strings );
|
||||
}
|
||||
|
||||
|
||||
String getLanguageName( String id )
|
||||
{
|
||||
if ( !this.hasLanguage( id ) ) {
|
||||
return null;
|
||||
}
|
||||
return this.languages.get( id ).getLanguageName( );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new language definition in the database.
|
||||
*
|
||||
* @param id
|
||||
* the new language's identifier
|
||||
* @param name
|
||||
* the new language's name
|
||||
* @return <code>true</code> on success, <code>false</code> otherwise
|
||||
*/
|
||||
boolean addLanguage( int administrator , String id , String name )
|
||||
{
|
||||
if ( this.hasLanguage( id ) ) {
|
||||
return false;
|
||||
}
|
||||
this.uocLanguage( administrator , id , name );
|
||||
this.languages.put( id , new LanguageStore( id , name ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Modifies the name of a language.
|
||||
*
|
||||
* @param administrator
|
||||
*
|
||||
* @param id
|
||||
* the language's identifier
|
||||
* @param name
|
||||
* the language's new name
|
||||
* @return null if the language does not exist, or the old name of the language if it does
|
||||
*/
|
||||
String updateLanguage( int administrator , String id , String name )
|
||||
{
|
||||
LanguageStore ls = this.languages.get( id );
|
||||
if ( ls == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String oldName = ls.getLanguageName( );
|
||||
this.uocLanguage( administrator , id , name );
|
||||
ls.setLanguageName( name );
|
||||
return oldName;
|
||||
}
|
||||
|
||||
|
||||
private void uocLanguage( final int administrator , final String id , final String name )
|
||||
{
|
||||
this.tTemplate.execute( new TransactionCallbackWithoutResult( ) {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult( TransactionStatus status )
|
||||
{
|
||||
fUocLanguage.execute( id , name , administrator );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accesses the {@link Translation} object for a given language/string identifier pair.
|
||||
*
|
||||
* @param language
|
||||
* the language's identifier
|
||||
* @param string
|
||||
* the string's identifier
|
||||
* @return the translation
|
||||
* @throws NullPointerException
|
||||
* if the language does not exist
|
||||
*/
|
||||
String getTranslation( String language , String string )
|
||||
{
|
||||
return this.languages.get( language ).getTranslation( string );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets or creates the translation for a given language/string identifier pair.
|
||||
*
|
||||
* @param administrator
|
||||
*
|
||||
* @param language
|
||||
* the language's identifier
|
||||
* @param string
|
||||
* the string's identifier
|
||||
* @param translation
|
||||
* the translated text for the string
|
||||
* @return the old translated text if the translation existed or null if a new translation was
|
||||
* created
|
||||
* @throws NullPointerException
|
||||
* if the language does not exist
|
||||
* @throws IllegalArgumentException
|
||||
* if the string does not exist
|
||||
*/
|
||||
String setTranslation( final int administrator , final String language , final String string , final String translation )
|
||||
{
|
||||
// Get existing translation
|
||||
LanguageStore store = this.languages.get( language );
|
||||
String old = store.getTranslation( string );
|
||||
if ( !this.strings.contains( string ) ) {
|
||||
throw new IllegalArgumentException( );
|
||||
}
|
||||
|
||||
// Create or update the database record
|
||||
this.tTemplate.execute( new TransactionCallbackWithoutResult( ) {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult( TransactionStatus status )
|
||||
{
|
||||
fUocTranslation.execute( language , string , translation , administrator );
|
||||
}
|
||||
} );
|
||||
|
||||
// Store the new translation
|
||||
store.addTranslation( string , translation );
|
||||
return old;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a string from scratch, storing initial translations along with it.
|
||||
*
|
||||
* @param string
|
||||
* the new string's identifier
|
||||
* @param translations
|
||||
* a language identifiers -> translated text map to use as the string's initial
|
||||
* translations.
|
||||
* @throws ConstraintViolationException
|
||||
* if the string identifier already existed in the DB
|
||||
* @throws NullPointerException
|
||||
* if one of the languages does not exist
|
||||
* @throws IllegalArgumentException
|
||||
* if the string exists
|
||||
*/
|
||||
void createString( final int administrator , final String string , final Map< String , String > translations )
|
||||
{
|
||||
if ( this.strings.contains( string ) ) {
|
||||
throw new IllegalArgumentException( );
|
||||
}
|
||||
|
||||
// Map language stores to translations
|
||||
Map< LanguageStore , String > nTrans = new HashMap< LanguageStore , String >( );
|
||||
for ( String lId : translations.keySet( ) ) {
|
||||
LanguageStore store = this.languages.get( lId );
|
||||
nTrans.put( store , translations.get( lId ) );
|
||||
}
|
||||
|
||||
// Update the database
|
||||
this.tTemplate.execute( new TransactionCallbackWithoutResult( ) {
|
||||
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult( TransactionStatus status )
|
||||
{
|
||||
for ( Map.Entry< String , String > entry : translations.entrySet( ) ) {
|
||||
fUocTranslation.execute( entry.getKey( ) , string , entry.getValue( ) , administrator );
|
||||
}
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
// Add the string definition to the local cache
|
||||
this.strings.add( string );
|
||||
|
||||
// Add the various translations to each language's store
|
||||
for ( Map.Entry< LanguageStore , String > entry : nTrans.entrySet( ) ) {
|
||||
entry.getKey( ).addTranslation( string , entry.getValue( ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @return the instance's read lock */
|
||||
@Override
|
||||
public Lock readLock( )
|
||||
{
|
||||
return this.lock.readLock( );
|
||||
}
|
||||
|
||||
|
||||
/** @return the instance's write lock */
|
||||
@Override
|
||||
public Lock writeLock( )
|
||||
{
|
||||
return this.lock.writeLock( );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package com.deepclone.lw.beans.i18n;
|
||||
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import com.deepclone.lw.interfaces.i18n.I18NAdministration;
|
||||
import com.deepclone.lw.interfaces.i18n.I18NManager;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The I18N manager bean creates the {@link I18NData} instance on initialisation, which it then
|
||||
* shares with translator beans and administrative session instances.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
public class I18NManagerBean
|
||||
implements I18NManager , InitializingBean
|
||||
{
|
||||
|
||||
/** Transaction manager interface */
|
||||
private TransactionTemplate tTemplate;
|
||||
|
||||
/** Database interface */
|
||||
private DataSource dataSource;
|
||||
|
||||
/** Data store shared amongst the translator beans and administrative session instances */
|
||||
private I18NData data;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the transaction manager interface (DI)
|
||||
*
|
||||
* @param manager
|
||||
* the transaction manager
|
||||
*/
|
||||
@Autowired( required = true )
|
||||
public void setTransactionManager( DataSourceTransactionManager manager )
|
||||
{
|
||||
this.tTemplate = new TransactionTemplate( manager );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the JBDC interface (DI)
|
||||
*
|
||||
* @param dSource
|
||||
* the data source
|
||||
*/
|
||||
@Autowired( required = true )
|
||||
public void setDataSource( DataSource dSource )
|
||||
{
|
||||
this.dataSource = dSource;
|
||||
}
|
||||
|
||||
|
||||
/** Creates the shared {@link I18NData} instance on initialisation */
|
||||
@Override
|
||||
public void afterPropertiesSet( )
|
||||
{
|
||||
this.data = new I18NData( this.dataSource , this.tTemplate );
|
||||
}
|
||||
|
||||
|
||||
/** @return the shared {@link I18NData} instance */
|
||||
I18NData getData( )
|
||||
{
|
||||
return this.data;
|
||||
}
|
||||
|
||||
|
||||
/* Documentation in I18NManager interface */
|
||||
@Override
|
||||
public I18NAdministration getAdminSession( int administrator )
|
||||
{
|
||||
return new I18NAdministrationImpl( this.data , administrator );
|
||||
}
|
||||
|
||||
|
||||
/* Documentation in I18NManager interface */
|
||||
@Override
|
||||
public void reload( )
|
||||
{
|
||||
this.data.writeLock( ).lock( );
|
||||
try {
|
||||
this.data.loadAll( );
|
||||
} finally {
|
||||
this.data.writeLock( ).unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package com.deepclone.lw.beans.i18n;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A language store encapsulates all data that defines a language - from the language definition
|
||||
* itself to the various translations available in this language.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
class LanguageStore
|
||||
implements Serializable
|
||||
{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String languageId;
|
||||
|
||||
private String languageName;
|
||||
|
||||
private final Map<String,String> translations = new HashMap< String , String >( );
|
||||
|
||||
|
||||
public LanguageStore( String languageId , String languageName )
|
||||
{
|
||||
this.languageId = languageId;
|
||||
this.languageName = languageName;
|
||||
}
|
||||
|
||||
|
||||
/** @return the store's language identifier */
|
||||
String getLanguageIdentifier( )
|
||||
{
|
||||
return this.languageId;
|
||||
}
|
||||
|
||||
|
||||
/** @return the name of the language encapsulated by the store */
|
||||
String getLanguageName( )
|
||||
{
|
||||
return this.languageName;
|
||||
}
|
||||
|
||||
|
||||
/** @return the amount of translations available from the store */
|
||||
int getTranslationsCount( )
|
||||
{
|
||||
return this.translations.size( );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* a string identifier
|
||||
* @return the translated string
|
||||
*/
|
||||
String getTranslation( String string )
|
||||
{
|
||||
return this.translations.get( string );
|
||||
}
|
||||
|
||||
|
||||
void addTranslation( String stringId , String translation )
|
||||
{
|
||||
this.translations.put( stringId , translation );
|
||||
}
|
||||
|
||||
|
||||
void setLanguageName( String name )
|
||||
{
|
||||
this.languageName = name;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package com.deepclone.lw.beans.i18n;
|
||||
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
|
||||
import com.deepclone.lw.sqld.i18n.Translation;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The I18N data loader transaction reads all string definitions and language definitions from the
|
||||
* database, creating {@link LanguageStore} instances for each language.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
class LoaderTransaction
|
||||
extends TransactionCallbackWithoutResult
|
||||
{
|
||||
|
||||
/** Database interface */
|
||||
private final SimpleJdbcTemplate dTemplate;
|
||||
|
||||
/** String definition map being initialised */
|
||||
private final HashSet< String > strings;
|
||||
|
||||
/** Language definition map being initialised */
|
||||
private final HashMap< String , LanguageStore > languages;
|
||||
|
||||
|
||||
/** Copies the required references */
|
||||
LoaderTransaction( SimpleJdbcTemplate dTemplate , HashSet< String > strings ,
|
||||
HashMap< String , LanguageStore > languages )
|
||||
{
|
||||
this.dTemplate = dTemplate;
|
||||
this.strings = strings;
|
||||
this.languages = languages;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The transaction loads all string definitions, storing them in the string definition map, then
|
||||
* loads all languages, creating a {@link LanguageStore} for each instance.
|
||||
*/
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult( TransactionStatus status )
|
||||
{
|
||||
String sql;
|
||||
RowMapper< Translation > mapper = new RowMapper< Translation >( ) {
|
||||
|
||||
@Override
|
||||
public Translation mapRow( ResultSet rs , int rowNum )
|
||||
throws SQLException
|
||||
{
|
||||
Translation t = new Translation( );
|
||||
t.setLanguageId( rs.getString( "language_id" ) );
|
||||
t.setLanguageName( rs.getString( "language_name" ) );
|
||||
t.setStringId( rs.getString( "string_id" ) );
|
||||
t.setTranslation( rs.getString( "translation" ) );
|
||||
return t;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
sql = "SELECT language_id , language_name , string_id , translation FROM defs.translations_view";
|
||||
for ( Translation trans : this.dTemplate.query( sql , mapper ) ) {
|
||||
this.strings.add( trans.getStringId( ) );
|
||||
|
||||
LanguageStore ls = this.languages.get( trans.getLanguageId( ) );
|
||||
if ( ls == null ) {
|
||||
ls = new LanguageStore( trans.getLanguageId( ) , trans.getLanguageName( ) );
|
||||
this.languages.put( trans.getLanguageId( ) , ls );
|
||||
}
|
||||
ls.addTranslation( trans.getStringId( ) , trans.getTranslation( ) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
package com.deepclone.lw.beans.i18n;
|
||||
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.deepclone.lw.interfaces.i18n.Translator;
|
||||
import com.deepclone.lw.interfaces.i18n.UnknownLanguageException;
|
||||
import com.deepclone.lw.interfaces.i18n.UnknownStringException;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The translator bean's implementation uses the contents of the {@link I18NData} instance, which it
|
||||
* only accesses in read-only mode.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
public class TranslatorBean
|
||||
implements Translator
|
||||
{
|
||||
private I18NData data;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the bean's I18N manager (DI)
|
||||
*
|
||||
* @param manager
|
||||
* the I18N manager to use
|
||||
*/
|
||||
@Autowired( required = true )
|
||||
public void setI18NManager( I18NManagerBean manager )
|
||||
{
|
||||
this.data = manager.getData( );
|
||||
}
|
||||
|
||||
|
||||
/* Documentation in Translator interface */
|
||||
@Override
|
||||
public Set< String > getSupportedLanguages( )
|
||||
{
|
||||
Set< String > supported = new HashSet< String >( );
|
||||
int sCount , lSize;
|
||||
|
||||
this.data.readLock( ).lock( );
|
||||
try {
|
||||
sCount = this.data.getStringsCount( );
|
||||
if ( sCount == 0 ) {
|
||||
return supported;
|
||||
}
|
||||
|
||||
for ( String lId : this.data.getLanguages( ) ) {
|
||||
lSize = this.data.getLanguageSize( lId );
|
||||
if ( lSize == sCount ) {
|
||||
supported.add( lId );
|
||||
}
|
||||
}
|
||||
|
||||
return supported;
|
||||
} finally {
|
||||
this.data.readLock( ).unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Documentation in Translator interface */
|
||||
@Override
|
||||
public boolean isLanguageSupported( String language )
|
||||
{
|
||||
int lSize , sCount;
|
||||
|
||||
this.data.readLock( ).lock( );
|
||||
try {
|
||||
lSize = this.data.getLanguageSize( language );
|
||||
sCount = this.data.getStringsCount( );
|
||||
|
||||
return ( sCount > 0 && lSize == sCount );
|
||||
} finally {
|
||||
this.data.readLock( ).unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Documentation in Translator interface */
|
||||
@Override
|
||||
public String translate( String language , String string )
|
||||
throws UnknownStringException , UnknownLanguageException
|
||||
{
|
||||
this.data.readLock( ).lock( );
|
||||
try {
|
||||
if ( !this.data.hasString( string ) ) {
|
||||
throw new UnknownStringException( string );
|
||||
}
|
||||
if ( !this.data.isLanguageComplete( language ) ) {
|
||||
throw new UnknownLanguageException( language );
|
||||
}
|
||||
return this.data.getTranslation( language , string );
|
||||
} finally {
|
||||
this.data.readLock( ).unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Documentation in Translator interface */
|
||||
@Override
|
||||
public String getLanguageName( String language )
|
||||
throws UnknownLanguageException
|
||||
{
|
||||
this.data.readLock( ).lock( );
|
||||
try {
|
||||
if ( !this.data.isLanguageComplete( language ) ) {
|
||||
throw new UnknownLanguageException( language );
|
||||
}
|
||||
return this.data.getLanguageName( language );
|
||||
} finally {
|
||||
this.data.readLock( ).unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<import resource="i18n/i18n-manager-bean.xml" />
|
||||
<import resource="i18n/i18n-translator-bean.xml" />
|
||||
|
||||
</beans>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<bean id="i18nManager" class="com.deepclone.lw.beans.i18n.I18NManagerBean" />
|
||||
|
||||
</beans>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<bean id="i18nTranslator" class="com.deepclone.lw.beans.i18n.TranslatorBean" />
|
||||
|
||||
</beans>
|
0
legacyworlds-server-beans-i18n/src/test/java/.empty
Normal file
0
legacyworlds-server-beans-i18n/src/test/java/.empty
Normal file
0
legacyworlds-server-beans-i18n/src/test/resources/.empty
Normal file
0
legacyworlds-server-beans-i18n/src/test/resources/.empty
Normal file
Reference in a new issue