package com.iambookmaster.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.PageContext;
import com.google.gwt.i18n.client.Constants.DefaultStringValue;
import com.google.gwt.i18n.client.Messages.DefaultMessage;
import com.iambookmaster.client.locale.AppConstants;
import com.iambookmaster.client.locale.AppMessages;
import com.iambookmaster.client.remote.RemotePanel;
import com.iambookmaster.server.tags.MessageTag;
public class LocalMessages implements ServletContextListener,Serializable{
private static final long serialVersionUID = 1L;
private static final Logger log = Logger.getLogger(LocalMessages.class.getName());
private static HashMap<String,Properties> constansts;
private static HashMap<String,Properties> messages;
private static Properties defaultConstansts;
private static Properties defaultMessages;
public static final String DEFAULT_LOCALE = "";
private static final String LOCALE_IN_SESSION = LocalMessages.class.getName();
public void contextDestroyed(ServletContextEvent arg0) {
}
public void contextInitialized(ServletContextEvent arg0) {
try {
defaultConstansts = getProps(AppConstants.class, DEFAULT_LOCALE);
defaultMessages = getProps(AppMessages.class, DEFAULT_LOCALE);
String locales = arg0.getServletContext().getInitParameter("locales");
if (locales != null) {
String[] locs = locales.split(",");
constansts = new HashMap<String, Properties>();
messages = new HashMap<String, Properties>();
for (String locale:locs) {
constansts.put(locale, getProps(AppConstants.class, locale));
messages.put(locale, getProps(AppMessages.class, locale));
}
}
} catch (Exception e) {
log.log(Level.SEVERE,"Cannot load locales");
log.log(Level.SEVERE,e.getMessage());
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
private Properties getProps(Class class1, String locale) throws IOException{
StringBuffer buffer = new StringBuffer(class1.getName().replace('.', '/'));
if (locale.length()>0) {
buffer.append('_');
buffer.append(locale);
}
buffer.append(".properties");
Properties props = new Properties();
InputStream stream = MessageTag.class.getClassLoader().getResourceAsStream(buffer.toString());
if (stream !=null) {
InputStreamReader reader = new InputStreamReader(stream,"UTF-8");
//this is for JDK 1.6
//props.load(reader);
//this is for JDK 1.5
load(props,reader);
reader.close();
stream.close();
}
if (DEFAULT_LOCALE.equals(locale)) {
Method[] methods = class1.getDeclaredMethods();
for (Method method:methods) {
if (props.containsKey(method.getName())==false) {
DefaultStringValue annotation = method.getAnnotation(DefaultStringValue.class);
if (annotation!=null) {
props.setProperty(method.getName(), annotation.value());
}
DefaultMessage defaultMessage = method.getAnnotation(DefaultMessage.class);
if (defaultMessage!=null) {
props.setProperty(method.getName(), defaultMessage.value());
}
}
}
}
return props;
}
public static String getConstant(String key,String locale) {
if (locale==null) {
return defaultConstansts.getProperty(key);
} else {
Properties properties = constansts.get(locale);
if (properties==null) {
throw new IllegalArgumentException("Unknown locale "+locale);
} else {
String val = properties.getProperty(key);
if (val==null) {
return defaultConstansts.getProperty(key);
} else {
return val;
}
}
}
}
public static String getMessageText(String key,String locale) {
if (locale==null) {
return defaultMessages.getProperty(key);
} else {
Properties properties = messages.get(locale);
if (properties==null) {
throw new IllegalArgumentException("Unknown locale "+locale);
} else {
String val = properties.getProperty(key);
if (val==null) {
return defaultMessages.getProperty(key);
} else {
return val;
}
}
}
}
public static String getMessage(String key,String locale,Object... args) {
String text = getMessageText(key, locale);
if (text==null) {
throw new IllegalArgumentException("Unknown message key:"+key);
} else {
int j = 0;
int i = text.indexOf("{",j);
if (i>=0) {
StringBuffer buffer = new StringBuffer(text);
while (true) {
int k = i+1;
j=buffer.indexOf("}",k);
if (j<0) {
//only {
throw new IllegalArgumentException("Unclosed argument (no '}') in message with key '"+key+"'");
}
try {
k = Integer.parseInt(buffer.substring(k, j));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Incorrect argument number '"+buffer.substring(i,j+1)+"' in message with key '"+key+"'");
}
j++;
if (k<0 || k>=args.length) {
throw new IllegalArgumentException("Argument "+k+" was not provided for message with key '"+key+"'");
}
String val;
if (args[k] instanceof String) {
val = (String) args[k];
} else if (args[k]==null){
val = "";
} else {
val = args[k].toString();
}
buffer.replace(i, j, val);
j = i+val.length();
i = buffer.indexOf("{",j);
if (i<0) {
break;
}
}
return buffer.toString();
} else if (args.length==0){
//no parameters
return text;
} else {
//wrong parameters
throw new IllegalArgumentException("Message with key '"+key+"' expects no arguments");
}
}
}
public static String getLocale(HttpServletRequest request,HttpServletResponse response) {
String locale = (String)request.getSession().getAttribute(LOCALE_IN_SESSION);
if (locale==null) {
locale = request.getParameter(RemotePanel.LOCALE_IN_REQUEST);
} else {
return locale;
}
if (locale==null) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie:cookies) {
if (cookie.getName().equals(LOCALE_IN_SESSION)) {
locale = cookie.getValue();
break;
}
}
}
}
if (locale==null) {
locale = request.getLocale().getLanguage();
}
if (constansts.containsKey(locale)) {
response.addCookie(new Cookie(LOCALE_IN_SESSION,locale));
request.getSession().setAttribute(LOCALE_IN_SESSION,locale);
return locale;
} else {
int i = locale.indexOf('_');
if (i<0) {
return null;
} else {
locale = locale.substring(0,i);
if (constansts.containsKey(locale)) {
response.addCookie(new Cookie(LOCALE_IN_SESSION,locale));
request.getSession().setAttribute(LOCALE_IN_SESSION,locale);
return locale;
} else {
return null;
}
}
}
}
public static String getLocale(PageContext pageContext) {
return getLocale((HttpServletRequest)pageContext.getRequest(),(HttpServletResponse)pageContext.getResponse());
}
@SuppressWarnings("unchecked")
public static <T> T getInstance(Class<T> class1,String locale) {
Class[] inters = class1.getInterfaces();
Class[] all = new Class[inters.length+1];
System.arraycopy(inters, 0, all, 0, inters.length);
all[inters.length] = class1;
Object proxy = java.lang.reflect.Proxy.newProxyInstance(
class1.getClassLoader(),
all,
new LocaleProxy(locale));
return (T)proxy;
}
public void initialize(String locale) throws IOException {
defaultConstansts = getProps(AppConstants.class, DEFAULT_LOCALE);
defaultMessages = getProps(AppMessages.class, DEFAULT_LOCALE);
constansts = new HashMap<String, Properties>();
messages = new HashMap<String, Properties>();
constansts.put(locale, getProps(AppConstants.class, locale));
messages.put(locale, getProps(AppMessages.class, locale));
}
/**
* It is just for JDK 1.5
* The source was takes from JDK 1.5 sources
* @param inStream
* @throws IOException
*/
private void load(Properties properties,Reader reader) throws IOException {
char[] convtBuf = new char[1024];
LineReader lr = new LineReader(reader);
int limit;
int keyLen;
int valueStart;
char c;
boolean hasSep;
boolean precedingBackslash;
while ((limit = lr.readLine()) >= 0) {
c = 0;
keyLen = 0;
valueStart = limit;
hasSep = false;
//System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
precedingBackslash = false;
while (keyLen < limit) {
c = lr.lineBuf[keyLen];
//need check if escaped.
if ((c == '=' || c == ':') && !precedingBackslash) {
valueStart = keyLen + 1;
hasSep = true;
break;
} else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
valueStart = keyLen + 1;
break;
}
if (c == '\\') {
precedingBackslash = !precedingBackslash;
} else {
precedingBackslash = false;
}
keyLen++;
}
while (valueStart < limit) {
c = lr.lineBuf[valueStart];
if (c != ' ' && c != '\t' && c != '\f') {
if (!hasSep && (c == '=' || c == ':')) {
hasSep = true;
} else {
break;
}
}
valueStart++;
}
String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
properties.put(key, value);
}
}
/**
* It is just for JDK 1.5
* The source was takes from JDK 1.5 sources
* Read in a "logical line" from an InputStream/Reader, skip all comment
* and blank lines and filter out those leading whitespace characters
* (\u0020, \u0009 and \u000c) from the beginning of a "natural line".
* Method returns the char length of the "logical line" and stores
* the line in "lineBuf".
*/
class LineReader {
public LineReader(Reader reader) {
this.reader = reader;
inCharBuf = new char[8192];
}
byte[] inByteBuf;
char[] inCharBuf;
char[] lineBuf = new char[1024];
int inLimit = 0;
int inOff = 0;
InputStream inStream;
Reader reader;
int readLine() throws IOException {
int len = 0;
char c = 0;
boolean skipWhiteSpace = true;
boolean isCommentLine = false;
boolean isNewLine = true;
boolean appendedLineBegin = false;
boolean precedingBackslash = false;
boolean skipLF = false;
while (true) {
if (inOff >= inLimit) {
inLimit = (inStream==null)?reader.read(inCharBuf)
:inStream.read(inByteBuf);
inOff = 0;
if (inLimit <= 0) {
if (len == 0 || isCommentLine) {
return -1;
}
return len;
}
}
if (inStream != null) {
//The line below is equivalent to calling a
//ISO8859-1 decoder.
c = (char) (0xff & inByteBuf[inOff++]);
} else {
c = inCharBuf[inOff++];
}
if (skipLF) {
skipLF = false;
if (c == '\n') {
continue;
}
}
if (skipWhiteSpace) {
if (c == ' ' || c == '\t' || c == '\f') {
continue;
}
if (!appendedLineBegin && (c == '\r' || c == '\n')) {
continue;
}
skipWhiteSpace = false;
appendedLineBegin = false;
}
if (isNewLine) {
isNewLine = false;
if (c == '#' || c == '!') {
isCommentLine = true;
continue;
}
}
if (c != '\n' && c != '\r') {
lineBuf[len++] = c;
if (len == lineBuf.length) {
int newLength = lineBuf.length * 2;
if (newLength < 0) {
newLength = Integer.MAX_VALUE;
}
char[] buf = new char[newLength];
System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
lineBuf = buf;
}
//flip the preceding backslash flag
if (c == '\\') {
precedingBackslash = !precedingBackslash;
} else {
precedingBackslash = false;
}
}
else {
// reached EOL
if (isCommentLine || len == 0) {
isCommentLine = false;
isNewLine = true;
skipWhiteSpace = true;
len = 0;
continue;
}
if (inOff >= inLimit) {
inLimit = (inStream==null)
?reader.read(inCharBuf)
:inStream.read(inByteBuf);
inOff = 0;
if (inLimit <= 0) {
return len;
}
}
if (precedingBackslash) {
len -= 1;
//skip the leading whitespace characters in following line
skipWhiteSpace = true;
appendedLineBegin = true;
precedingBackslash = false;
if (c == '\r') {
skipLF = true;
}
} else {
return len;
}
}
}
}
}
/**
* It is just for JDK 1.5
* The source was takes from JDK 1.5 sources
* Converts encoded \uxxxx to unicode chars
* and changes special saved chars to their original forms
*/
private String loadConvert (char[] in, int off, int len, char[] convtBuf) {
if (convtBuf.length < len) {
int newLen = len * 2;
if (newLen < 0) {
newLen = Integer.MAX_VALUE;
}
convtBuf = new char[newLen];
}
char aChar;
char[] out = convtBuf;
int outLen = 0;
int end = off + len;
while (off < end) {
aChar = in[off++];
if (aChar == '\\') {
aChar = in[off++];
if(aChar == 'u') {
// Read the xxxx
int value=0;
for (int i=0; i<4; i++) {
aChar = in[off++];
switch (aChar) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
value = (value << 4) + aChar - '0';
break;
case 'a': case 'b': case 'c':
case 'd': case 'e': case 'f':
value = (value << 4) + 10 + aChar - 'a';
break;
case 'A': case 'B': case 'C':
case 'D': case 'E': case 'F':
value = (value << 4) + 10 + aChar - 'A';
break;
default:
throw new IllegalArgumentException(
"Malformed \\uxxxx encoding.");
}
}
out[outLen++] = (char)value;
} else {
if (aChar == 't') aChar = '\t';
else if (aChar == 'r') aChar = '\r';
else if (aChar == 'n') aChar = '\n';
else if (aChar == 'f') aChar = '\f';
out[outLen++] = aChar;
}
} else {
out[outLen++] = (char)aChar;
}
}
return new String (out, 0, outLen);
}
}