blob: d88c2291451f52d155315f4dc0b5b1002f06ee18 [file] [log] [blame]
package org.bouncycastle.i18n;
import org.bouncycastle.i18n.filter.Filter;
import org.bouncycastle.i18n.filter.TrustedInput;
import org.bouncycastle.i18n.filter.UntrustedInput;
import org.bouncycastle.i18n.filter.UntrustedUrlInput;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.text.Format;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.TimeZone;
public class LocalizedMessage
{
protected final String id;
protected final String resource;
// ISO-8859-1 is the default encoding
public static final String DEFAULT_ENCODING = "ISO-8859-1";
protected String encoding = DEFAULT_ENCODING;
protected FilteredArguments arguments;
protected FilteredArguments extraArgs = null;
protected Filter filter = null;
protected ClassLoader loader = null;
/**
* Constructs a new LocalizedMessage using <code>resource</code> as the base name for the
* RessourceBundle and <code>id</code> as the message bundle id the resource file.
* @param resource base name of the resource file
* @param id the id of the corresponding bundle in the resource file
* @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
*/
public LocalizedMessage(String resource,String id) throws NullPointerException
{
if (resource == null || id == null)
{
throw new NullPointerException();
}
this.id = id;
this.resource = resource;
arguments = new FilteredArguments();
}
/**
* Constructs a new LocalizedMessage using <code>resource</code> as the base name for the
* RessourceBundle and <code>id</code> as the message bundle id the resource file.
* @param resource base name of the resource file
* @param id the id of the corresponding bundle in the resource file
* @param encoding the encoding of the resource file
* @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
* @throws UnsupportedEncodingException if the encoding is not supported
*/
public LocalizedMessage(String resource,String id, String encoding) throws NullPointerException, UnsupportedEncodingException
{
if (resource == null || id == null)
{
throw new NullPointerException();
}
this.id = id;
this.resource = resource;
arguments = new FilteredArguments();
if (!Charset.isSupported(encoding))
{
throw new UnsupportedEncodingException("The encoding \"" + encoding + "\" is not supported.");
}
this.encoding = encoding;
}
/**
* Constructs a new LocalizedMessage using <code>resource</code> as the base name for the
* RessourceBundle and <code>id</code> as the message bundle id the resource file.
* @param resource base name of the resource file
* @param id the id of the corresponding bundle in the resource file
* @param arguments an array containing the arguments for the message
* @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
*/
public LocalizedMessage(String resource, String id, Object[] arguments) throws NullPointerException
{
if (resource == null || id == null || arguments == null)
{
throw new NullPointerException();
}
this.id = id;
this.resource = resource;
this.arguments = new FilteredArguments(arguments);
}
/**
* Constructs a new LocalizedMessage using <code>resource</code> as the base name for the
* RessourceBundle and <code>id</code> as the message bundle id the resource file.
* @param resource base name of the resource file
* @param id the id of the corresponding bundle in the resource file
* @param encoding the encoding of the resource file
* @param arguments an array containing the arguments for the message
* @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
* @throws UnsupportedEncodingException if the encoding is not supported
*/
public LocalizedMessage(String resource, String id, String encoding, Object[] arguments) throws NullPointerException, UnsupportedEncodingException
{
if (resource == null || id == null || arguments == null)
{
throw new NullPointerException();
}
this.id = id;
this.resource = resource;
this.arguments = new FilteredArguments(arguments);
if (!Charset.isSupported(encoding))
{
throw new UnsupportedEncodingException("The encoding \"" + encoding + "\" is not supported.");
}
this.encoding = encoding;
}
/**
* Reads the entry <code>id + "." + key</code> from the resource file and returns a
* formated message for the given Locale and TimeZone.
* @param key second part of the entry id
* @param loc the used {@link Locale}
* @param timezone the used {@link TimeZone}
* @return a Strng containing the localized message
* @throws MissingEntryException if the resource file is not available or the entry does not exist.
*/
public String getEntry(String key,Locale loc, TimeZone timezone) throws MissingEntryException
{
String entry = id;
if (key != null)
{
entry += "." + key;
}
try
{
ResourceBundle bundle;
if (loader == null)
{
bundle = ResourceBundle.getBundle(resource,loc);
}
else
{
bundle = ResourceBundle.getBundle(resource, loc, loader);
}
String result = bundle.getString(entry);
if (!encoding.equals(DEFAULT_ENCODING))
{
result = new String(result.getBytes(DEFAULT_ENCODING), encoding);
}
if (!arguments.isEmpty())
{
result = formatWithTimeZone(result,arguments.getFilteredArgs(loc),loc,timezone);
}
result = addExtraArgs(result, loc);
return result;
}
catch (MissingResourceException mre)
{
throw new MissingEntryException("Can't find entry " + entry + " in resource file " + resource + ".",
resource,
entry,
loc,
loader != null ? loader : this.getClassLoader());
}
catch (UnsupportedEncodingException use)
{
// should never occur - cause we already test this in the constructor
throw new RuntimeException(use);
}
}
protected String formatWithTimeZone(
String template,
Object[] arguments,
Locale locale,
TimeZone timezone)
{
MessageFormat mf = new MessageFormat(" ");
mf.setLocale(locale);
mf.applyPattern(template);
if (!timezone.equals(TimeZone.getDefault()))
{
Format[] formats = mf.getFormats();
for (int i = 0; i < formats.length; i++)
{
if (formats[i] instanceof DateFormat)
{
DateFormat temp = (DateFormat) formats[i];
temp.setTimeZone(timezone);
mf.setFormat(i,temp);
}
}
}
return mf.format(arguments);
}
protected String addExtraArgs(String msg, Locale locale)
{
if (extraArgs != null)
{
StringBuffer sb = new StringBuffer(msg);
Object[] filteredArgs = extraArgs.getFilteredArgs(locale);
for (int i = 0; i < filteredArgs.length; i++)
{
sb.append(filteredArgs[i]);
}
msg = sb.toString();
}
return msg;
}
/**
* Sets the {@link Filter} that is used to filter the arguments of this message
* @param filter the {@link Filter} to use. <code>null</code> to disable filtering.
*/
public void setFilter(Filter filter)
{
arguments.setFilter(filter);
if (extraArgs != null)
{
extraArgs.setFilter(filter);
}
this.filter = filter;
}
/**
* Returns the current filter.
* @return the current filter
*/
public Filter getFilter()
{
return filter;
}
/**
* Set the {@link ClassLoader} which loads the resource files. If it is set to <code>null</code>
* then the default {@link ClassLoader} is used.
* @param loader the {@link ClassLoader} which loads the resource files
*/
public void setClassLoader(ClassLoader loader)
{
this.loader = loader;
}
/**
* Returns the {@link ClassLoader} which loads the resource files or <code>null</code>
* if the default ClassLoader is used.
* @return the {@link ClassLoader} which loads the resource files
*/
public ClassLoader getClassLoader()
{
return loader;
}
/**
* Returns the id of the message in the resource bundle.
* @return the id of the message
*/
public String getId()
{
return id;
}
/**
* Returns the name of the resource bundle for this message
* @return name of the resource file
*/
public String getResource()
{
return resource;
}
/**
* Returns an <code>Object[]</code> containing the message arguments.
* @return the message arguments
*/
public Object[] getArguments()
{
return arguments.getArguments();
}
/**
*
* @param extraArg
*/
public void setExtraArgument(Object extraArg)
{
setExtraArguments(new Object[] {extraArg});
}
/**
*
* @param extraArgs
*/
public void setExtraArguments(Object[] extraArgs)
{
if (extraArgs != null)
{
this.extraArgs = new FilteredArguments(extraArgs);
this.extraArgs.setFilter(filter);
}
else
{
this.extraArgs = null;
}
}
/**
*
* @return
*/
public Object[] getExtraArgs()
{
return (extraArgs == null) ? null : extraArgs.getArguments();
}
protected class FilteredArguments
{
protected static final int NO_FILTER = 0;
protected static final int FILTER = 1;
protected static final int FILTER_URL = 2;
protected Filter filter = null;
protected boolean[] isLocaleSpecific;
protected int[] argFilterType;
protected Object[] arguments;
protected Object[] unpackedArgs;
protected Object[] filteredArgs;
FilteredArguments()
{
this(new Object[0]);
}
FilteredArguments(Object[] args)
{
this.arguments = args;
this.unpackedArgs = new Object[args.length];
this.filteredArgs = new Object[args.length];
this.isLocaleSpecific = new boolean[args.length];
this.argFilterType = new int[args.length];
for (int i = 0; i < args.length; i++)
{
if (args[i] instanceof TrustedInput)
{
this.unpackedArgs[i] = ((TrustedInput) args[i]).getInput();
argFilterType[i] = NO_FILTER;
}
else if (args[i] instanceof UntrustedInput)
{
this.unpackedArgs[i] = ((UntrustedInput) args[i]).getInput();
if (args[i] instanceof UntrustedUrlInput)
{
argFilterType[i] = FILTER_URL;
}
else
{
argFilterType[i] = FILTER;
}
}
else
{
this.unpackedArgs[i] = args[i];
argFilterType[i] = FILTER;
}
// locale specific
this.isLocaleSpecific[i] = (this.unpackedArgs[i] instanceof LocaleString);
}
}
public boolean isEmpty()
{
return unpackedArgs.length == 0;
}
public Object[] getArguments()
{
return arguments;
}
public Object[] getFilteredArgs(Locale locale)
{
Object[] result = new Object[unpackedArgs.length];
for (int i = 0; i < unpackedArgs.length; i++)
{
Object arg;
if (filteredArgs[i] != null)
{
arg = filteredArgs[i];
}
else
{
arg = unpackedArgs[i];
if (isLocaleSpecific[i])
{
// get locale
arg = ((LocaleString) arg).getLocaleString(locale);
arg = filter(argFilterType[i], arg);
}
else
{
arg = filter(argFilterType[i], arg);
filteredArgs[i] = arg;
}
}
result[i] = arg;
}
return result;
}
private Object filter(int type, Object obj)
{
if (filter != null)
{
Object o = (null == obj) ? "null" : obj;
switch (type)
{
case NO_FILTER:
return o;
case FILTER:
return filter.doFilter(o.toString());
case FILTER_URL:
return filter.doFilterUrl(o.toString());
default:
return null;
}
}
else
{
return obj;
}
}
public Filter getFilter()
{
return filter;
}
public void setFilter(Filter filter)
{
if (filter != this.filter)
{
for (int i = 0; i < unpackedArgs.length; i++)
{
filteredArgs[i] = null;
}
}
this.filter = filter;
}
}
public String toString()
{
StringBuffer sb = new StringBuffer();
sb.append("Resource: \"").append(resource);
sb.append("\" Id: \"").append(id).append("\"");
sb.append(" Arguments: ").append(arguments.getArguments().length).append(" normal");
if (extraArgs != null && extraArgs.getArguments().length > 0)
{
sb.append(", ").append(extraArgs.getArguments().length).append(" extra");
}
sb.append(" Encoding: ").append(encoding);
sb.append(" ClassLoader: ").append(loader);
return sb.toString();
}
}