package eu.irreality.age.messages;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import bsh.This;
import eu.irreality.age.NaturalLanguage;
import eu.irreality.age.ReturnValue;
import eu.irreality.age.World;
import eu.irreality.age.debug.Debug;
import eu.irreality.age.debug.ExceptionPrinter;
import eu.irreality.age.filemanagement.Paths;
import eu.irreality.age.scripting.ScriptException;
import eu.irreality.age.util.UTF8PropertiesLoader;
public class Messages
{
private static Map defaultInstances = Collections.synchronizedMap( new HashMap() ); //String (language code) -> Messages & World -> Messages
//synchronization because this map is accessed by game engine threads and by the world-end thread (the latter to remove instances)
private Properties properties;
//this map stores messages that are modified temporarily (for one use only).
private Map tempChanged = Collections.synchronizedMap( new HashMap() );
public static String defaultMessagePath = Paths.LANG_FILES_PATH + "/messages.lan";
public static String getPathForLanguage ( String languageCode )
{
return Paths.LANG_FILES_PATH + "/" + languageCode + "/messages.lan";
}
/**
* If a Messages instance is associated to a particular world, then the getMessage() method will attempt to call scripting code
* in the world to generate custom messages.
*/
private World world = null;
public void setWorld ( World w )
{
this.world = w;
}
/*
public static void printReport()
{
System.err.println(defaultInstances);
System.err.println(defaultInstances.keySet());
System.err.println(defaultInstances.values());
}
*/
public Messages(String path)
{
properties = new Properties();
try
{
//InputStream is = this.getClass().getClassLoader().getResourceAsStream("org/f2o/absurdum/puck/i18n/Messages.properties");
URL u = this.getClass().getClassLoader().getResource(path);
InputStream is = u.openStream();
if ( is == null ) throw new IOException("Could not read message file " + u);
//properties.load( new InputStreamReader ( is , "UTF-8" ) );
//1.5 compatible:
//TODO: apply this cheap hack only if java version < 1.6
UTF8PropertiesLoader.loadProperties(properties,is,"UTF-8");
}
catch ( IOException ioe )
{
ioe.printStackTrace();
}
}
public Messages(URL u) throws IOException
{
properties = new Properties();
InputStream is = u.openStream();
if ( is == null ) throw new IOException("Could not read message file " + u);
//properties.load( new InputStreamReader ( is , "UTF-8" ) );
//1.5 compatible:
//TODO: apply this cheap hack only if java version < 1.6
UTF8PropertiesLoader.loadProperties(properties,is,"UTF-8");
is.close();
}
private String getEntryForKey ( String key )
{
String tempChangedEntry = (String) tempChanged.get(key);
if ( tempChangedEntry != null )
{
tempChanged.remove(key); //remove temporary mapping
return tempChangedEntry;
}
return properties.getProperty(key);
}
public String getMessage ( String key )
{
String mess = getEntryForKey ( key );
if ( mess == null && !(this == getDefaultInstance(world)) ) mess = getDefaultInstance(world).getMessage(key); //fallback to default instance
return ( mess != null ? mess : "??" + key + "??" );
}
/**
* The parameter argumentsForScriptCode is passed to the beanshell code so the user can employ it to generate custom messages
* if necessary.
*/
public String getMessage ( String key , Object[] argumentsForScriptCode )
{
if ( world != null )
{
try
{
ReturnValue retval = new ReturnValue(null);
world.execCode( "getMessage" , new Object[] { key , argumentsForScriptCode } , retval );
if ( retval.getRetVal() != null ) return (String)retval.getRetVal();
}
catch ( ScriptException bshte)
{
world.writeError("bsh.TargetError found at getMessage routine\n" );
world.writeError(ExceptionPrinter.getExceptionReport(bshte));
world.writeError( bshte.printTargetError(bshte) );
}
}
String mess = getEntryForKey ( key );
if ( mess == null && !(this == getDefaultInstance(world)) ) mess = getDefaultInstance(world).getMessage(key,argumentsForScriptCode); //fallback
return ( mess != null ? mess : "??" + key + "??" );
}
/**
* Returns the default repository of messages for a given language code. The same instance is always returned for the same language code.
* @param languageCode
* @return
*/
public static Messages getDefaultInstance(String languageCode)
{
Messages cached = (Messages) defaultInstances.get(languageCode);
if ( cached == null )
{
cached = new Messages(getPathForLanguage(languageCode));
defaultInstances.put(languageCode,cached);
}
return cached;
}
/**
* Returns the default repository of messages provided by AGE for a given world, i.e., the repository of messages for the language code associated with the
* language of that world, and with a pointer to the world.
* @param w
* @return
*/
public static Messages getDefaultInstance(World w)
{
Messages cached = (Messages) defaultInstances.get(w);
if ( cached != null ) return cached;
String languageCode = null;
if ( w != null ) languageCode = w.getLanguage().getLanguageCode();
if ( languageCode == null ) languageCode = NaturalLanguage.DEFAULT_LANGUAGE_CODE;
//return getDefaultInstance ( languageCode );
Messages theInstance = new Messages(getPathForLanguage(languageCode));
theInstance.setWorld(w);
defaultInstances.put(w,theInstance);
//System.err.println("Caching: " + w + "->" + theInstance);
//new Throwable().printStackTrace();
return theInstance;
}
/**
* Removes entry for world w from the cache if it is present.
* @param w
*/
public static void clearCache ( World w )
{
Messages cached = (Messages) defaultInstances.get(w);
if ( cached != null )
{
//System.err.println("Found cached entry: " + cached + " for world " + w);
cached.setWorld(null);
}
//else
// System.err.println("Not found cached entry for world " + w);
defaultInstances.remove(w);
}
public void setMessage ( String key , String message )
{
properties.setProperty( key , message );
}
public void setNextMessage ( String key , String message )
{
tempChanged.put(key,message);
}
public String getMessage ( String key , String placeholder , String substitution )
{
return buildMessage ( getMessage(key) , placeholder , substitution );
}
public String getMessage ( String key , String p1 , String s1 , String p2 , String s2 )
{
return buildMessage ( getMessage(key) , p1 , s1 , p2 , s2 );
}
public String getMessage ( String key , String p1 , String s1 , String p2 , String s2 , String p3 , String s3 )
{
return buildMessage ( getMessage(key) , p1 , s1 , p2 , s2 , p3 , s3 );
}
public String getMessage ( String key , String p1 , String s1 , String p2 , String s2 , String p3 , String s3 , String p4 , String s4 )
{
return buildMessage ( getMessage(key) , p1 , s1 , p2 , s2 , p3 , s3 , p4 , s4 );
}
public String getMessage ( String key , String placeholder , String substitution , Object[] argumentsForScriptCode )
{
return buildMessage ( getMessage(key,argumentsForScriptCode) , placeholder , substitution );
}
public String getMessage ( String key , String p1 , String s1 , String p2 , String s2 , Object[] argumentsForScriptCode )
{
return buildMessage ( getMessage(key,argumentsForScriptCode) , p1 , s1 , p2 , s2 );
}
public String getMessage ( String key , String p1 , String s1 , String p2 , String s2 , String p3 , String s3 , Object[] argumentsForScriptCode )
{
return buildMessage ( getMessage(key,argumentsForScriptCode) , p1 , s1 , p2 , s2 , p3 , s3 );
}
public String getMessage ( String key , String p1 , String s1 , String p2 , String s2 , String p3 , String s3 , String p4 , String s4 , Object[] argumentsForScriptCode )
{
return buildMessage ( getMessage(key,argumentsForScriptCode) , p1 , s1 , p2 , s2 , p3 , s3 , p4 , s4 );
}
/**
* buildMessage ( "Coges $object", "$object", "la espada" ) returns "Coges la espada".
* @param messString
* @param placeholder
* @return
*/
public static String buildMessage ( String messString , String placeholder , String substitution )
{
return messString.replace(placeholder,substitution);
}
public static String buildMessage ( String messString , String p1 , String s1 , String p2 , String s2 )
{
return buildMessage ( buildMessage ( messString,p1,s1 ) , p2 , s2 );
}
public static String buildMessage ( String messString , String p1 , String s1 , String p2 , String s2 , String p3 , String s3 )
{
return buildMessage ( buildMessage ( messString,p1,s1,p2,s2 ) , p3 , s3 );
}
public static String buildMessage ( String messString , String p1 , String s1 , String p2 , String s2 , String p3 , String s3 , String p4 , String s4 )
{
return buildMessage ( buildMessage ( messString,p1,s1,p2,s2,p3,s3 ) , p4 , s4 );
}
}