Package com.habitsoft.kiyaa.server

Source Code of com.habitsoft.kiyaa.server.ServerLocalization$ImplKey

package com.habitsoft.kiyaa.server;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.TreeMap;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.CtField.Initializer;

import com.google.gwt.i18n.client.Constants.DefaultBooleanValue;
import com.google.gwt.i18n.client.Constants.DefaultDoubleValue;
import com.google.gwt.i18n.client.Constants.DefaultFloatValue;
import com.google.gwt.i18n.client.Constants.DefaultIntValue;
import com.google.gwt.i18n.client.Constants.DefaultStringValue;
import com.google.gwt.i18n.client.LocalizableResource.GenerateKeys;
import com.google.gwt.i18n.client.LocalizableResource.Key;
import com.google.gwt.i18n.client.LocalizableResource.Meaning;
import com.google.gwt.i18n.client.Messages.DefaultMessage;
import com.google.gwt.i18n.rebind.keygen.KeyGenerator;
import com.google.gwt.i18n.rebind.keygen.MethodNameKeyGenerator;

/**
* Use javassist to mimic the static localization features of GWT.
*
* Code on the server can instantiate the same Messages and Constants
* subclasses as on the client, and this code will load the appropriate
* properties files and use the @DefaultStringValue annotations of the
* class (if present).
*
*/
public class ServerLocalization {
    private static ClassPool classPool;
   
    static class ImplKey {
        String className;
        Locale locale;
        public ImplKey(String className, Locale locale) {
            this.className = className;
            this.locale = locale;
        }
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((className == null) ? 0 : className.hashCode());
            result = prime * result + ((locale == null) ? 0 : locale.hashCode());
            return result;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (!(obj instanceof ImplKey))
                return false;
            ImplKey other = (ImplKey) obj;
            if (className == null) {
                if (other.className != null)
                    return false;
            } else if (!className.equals(other.className))
                return false;
            if (locale == null) {
                if (other.locale != null)
                    return false;
            } else if (!locale.equals(other.locale))
                return false;
            return true;
        }
       
    }
    static Map<ImplKey,Object> cache = new HashMap<ImplKey, Object>();
   
    private static ClassPool getClassPool() {
        if(classPool == null) {
            classPool = ClassPool.getDefault(); //new ClassPool();
        }
        return classPool;
    }
    /**
     * Create a class like:
     *
     * <code>
     * class SomeMessagesImpl extends TreeMap<String,MessageFormat> implements SomeMessages {
     *     ResourceBundle res;
     *     public SomeMessagesImpl(ResourceBundle res) {
     *         this.res = res;
     *     }
     *
     *     public String someMessage(int a, Object b, String c) {
     *         get("someMessage").format(new Object[]{a, b, c}, new StringBuffer(), null).toString();
     *     }
     * }
     * </code>
     *
     * And return an instance of it populated with the appropriate MessageFormat objects.
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static <T> T createMessagesInstance(Class<T> messagesClass, Locale locale) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        ImplKey cacheKey = new ImplKey(messagesClass.getName(), locale);
        T result = (T) cache.get(cacheKey);
        if(result != null) {
            return result;
        }
        String ifaceName = messagesClass.getName();
        final ClassPool classPool = getClassPool();
        try {
            CtClass iface = classPool.get(messagesClass.getName());
            CtClass messageFormat = classPool.get(MessageFormat.class.getName());
            CtClass stringClass = classPool.get(String.class.getName());
            CtClass localeClass = classPool.get(Locale.class.getName());
            CtClass impl = classPool.makeClass(ifaceName+"Impl_"+locale.toString());
            impl.addInterface(iface);
           
            KeyGenerator keyGenerator = new MethodNameKeyGenerator();
            for(Object ann : iface.getAnnotations()) {
                if(ann instanceof GenerateKeys) {
                    String keyGeneratorClassName = ((GenerateKeys)ann).value();
                    Class<KeyGenerator> keyGeneratorClass;
                    try {
                        keyGeneratorClass = (Class<KeyGenerator>) Class.forName(keyGeneratorClassName);
                    } catch(ClassNotFoundException cnfe) {
                        keyGeneratorClass = (Class<KeyGenerator>) Class.forName(KeyGenerator.class.getName()+"."+keyGeneratorClassName);
                    }
                    keyGenerator = keyGeneratorClass.newInstance();
                }
            }
           
            ResourceBundle resBundle = ResourceBundle.getBundle(ifaceName, locale);
            CtConstructor ctorMethod = new CtConstructor(new CtClass[] {localeClass}, impl);
            impl.addConstructor(ctorMethod);
            ctorMethod.setModifiers(Modifier.PUBLIC);
            ctorMethod.setBody("super();");
            CtField localeField = new CtField(localeClass, "_locale", impl);
            impl.addField(localeField, CtField.Initializer.byParameter(0));
            TreeMap<String,String> keys = new TreeMap<String,String>();
            for(CtMethod method : iface.getMethods()) {
                // Only abstract methods
                if((method.getModifiers() & Modifier.ABSTRACT) == 0)
                    continue;
                CtMethod methodImpl = new CtMethod(method, impl, null);
                String methodKey = null;
                String meaning = null;
                String defaultValue = null;
                for(Object ann : method.getAnnotations()) {
                    if(ann instanceof Key) {
                        methodKey = ((Key)ann).value();
                    } else if(ann instanceof Meaning) {
                        meaning = ((Meaning)ann).value();
                    } else if(ann instanceof DefaultStringValue) {
                        defaultValue = ((DefaultStringValue)ann).value();
                    } else if(ann instanceof DefaultBooleanValue) {
                        defaultValue = String.valueOf(((DefaultBooleanValue)ann).value());
                    } else if(ann instanceof DefaultDoubleValue) {
                        defaultValue = String.valueOf(((DefaultDoubleValue)ann).value());
                    } else if(ann instanceof DefaultMessage) {
                        defaultValue = ((DefaultMessage)ann).value();
                    }
                }
                if(methodKey == null) {
                    methodKey = keyGenerator.generateKey(ifaceName, method.getName(), defaultValue, meaning);
                }
                String value;
                try {
                    value = resBundle.getString(methodKey);
                } catch (java.util.MissingResourceException mre) {
                    if(defaultValue != null) {
                        value = defaultValue;
                    } else throw mre;
                }
                CtField patternField = new CtField(stringClass, method.getName()+"Pattern", impl);
                impl.addField(patternField, CtField.Initializer.constant(value));
                CtField formatField = new CtField(messageFormat, method.getName()+"MessageFormat", impl);
                impl.addField(formatField, CtField.Initializer.byExpr("new java.text.MessageFormat("+patternField.getName()+", _locale)"));
                methodImpl.setModifiers(Modifier.PUBLIC);
                methodImpl.setBody("return "+formatField.getName()+".format($args, new StringBuffer(), null).toString();");
                impl.addMethod(methodImpl);
                keys.put(methodKey, defaultValue);
            }
            final Class implClazz = impl.toClass();
            final Constructor ctor = implClazz.getConstructor(Locale.class);
            Object instance = ctor.newInstance(locale);
            cache.put(cacheKey, instance);
            return messagesClass.cast(instance);
           
        } catch (NotFoundException e) {
            throw new Error(e);
        } catch (RuntimeException e) {
            throw new Error(e);
        } catch (CannotCompileException e) {
            throw new Error(e);
        } catch (NoSuchMethodException e) {
            throw new Error(e);
        } catch (InvocationTargetException e) {
            throw new Error(e);
        }
       
    }
   
    public static Map<String, String> getMap(ResourceBundle res, String methodName) throws MissingResourceException {
        String[] keys = getStringArray(res, methodName);
        Map<String,String> result = new TreeMap<String, String>();
        for(String key : keys) {
            result.put(key, res.getString(key));
        }
        return result;
    }

    public static String[] getStringArray(ResourceBundle res, String methodName) throws MissingResourceException {
        String s = res.getString(methodName);
        String[] parts = s.split("(?<!\\\\),");
        for(int i=0; i < parts.length; i++) {
            parts[i] = parts[i].replaceAll("\\\\(.)", "$1").trim();
        }
        return parts;
    }
   
    /**
     * Create a class like:
     *
     * <code>
     * class SomeConstantsImpl extends BaseConstantsImpl implements SomeConstants {
     *     String _someConstant;
     *     int _intConstant;
     *     double _doubleConstant;
     *     SomeConstantsImpl(ResourceBundle res) {
     *        super(res);
     *        _someConstant = getString("someConstant");
     *        _intConstant = getInt("intConstant");
     *        _doubleConstant = getDouble("doubleConstant");
     *     }
     *    
     *     public String someConstant() {
     *        return _someConstant;
     *     }
     *     public int intConstant() {
     *        return _intConstant;
     *     }
     *     public double doubleConstant() {
     *        return _doubleConstant;
     *     }
     * }
     *
     * Then lookup the resource bundle for the given locale and construct
     * and instance of this new class with that resource bundle.
     */
    public static <T> T createConstantsInstance(Class<T> constantsClass, Locale locale) {
        ImplKey key = new ImplKey(constantsClass.getName(), locale);
        T result = (T) cache.get(key);
        if(result != null) {
            return result;
        }
        String ifaceName = constantsClass.getName();
        final ClassPool classPool = getClassPool();
        try {
            CtClass iface = classPool.get(constantsClass.getName());
            CtClass baseConstantsImpl = classPool.get(BaseConstantsImpl.class.getName());
            CtClass stringClass = classPool.get(String.class.getName());
            CtClass impl = classPool.makeClass(ifaceName+"Impl_"+locale.toString(), baseConstantsImpl);
            impl.addInterface(iface);
            final CtClass resourceBundle = classPool.get(ResourceBundle.class.getName());
            CtField resField = new CtField(resourceBundle, "res", impl);
            impl.addField(resField);
           
            KeyGenerator keyGenerator = new MethodNameKeyGenerator();
            for(Object ann : iface.getAnnotations()) {
                if(ann instanceof GenerateKeys) {
                    String keyGeneratorClassName = ((GenerateKeys)ann).value();
                    Class<KeyGenerator> keyGeneratorClass;
                    try {
                        keyGeneratorClass = (Class<KeyGenerator>) Class.forName(keyGeneratorClassName);
                    } catch(ClassNotFoundException cnfe) {
                        keyGeneratorClass = (Class<KeyGenerator>) Class.forName(KeyGenerator.class.getName()+"."+keyGeneratorClassName);
                    }
                    keyGenerator = keyGeneratorClass.newInstance();
                }
            }
           
            ResourceBundle resBundle = ResourceBundle.getBundle(ifaceName, locale);
            for(CtMethod method : iface.getMethods()) {
                // Only abstract methods
                if((method.getModifiers() & Modifier.ABSTRACT) == 0)
                    continue;
                String methodKey = null;
                String meaning = null;
                Object defaultValue = null;
                for(Object ann : method.getAnnotations()) {
                    if(ann instanceof Key) {
                        methodKey = ((Key)ann).value();
                    } else if(ann instanceof Meaning) {
                        meaning = ((Meaning)ann).value();
                    } else if(ann instanceof DefaultStringValue) {
                        defaultValue = ((DefaultStringValue)ann).value();
                    } else if(ann instanceof DefaultBooleanValue) {
                        defaultValue = ((DefaultBooleanValue)ann).value();
                    } else if(ann instanceof DefaultDoubleValue) {
                        defaultValue = ((DefaultDoubleValue)ann).value();
                    } else if(ann instanceof DefaultIntValue) {
                        defaultValue = ((DefaultIntValue)ann).value();
                    } else if(ann instanceof DefaultFloatValue) {
                        defaultValue = ((DefaultFloatValue)ann).value();
                    } else if(ann instanceof DefaultMessage) {
                        defaultValue = ((DefaultMessage)ann).value();
                    }
                }
                if(methodKey == null) {
                    methodKey = keyGenerator.generateKey(ifaceName, method.getName(), String.valueOf(defaultValue), meaning);
                }
                CtMethod methodImpl = new CtMethod(method, impl, null);
                CtClass returnType = methodImpl.getReturnType();
                Initializer initializer;
                String valueString;
                try {
                    valueString = resBundle.getString(methodKey);
                } catch(MissingResourceException mre) {
                    if(defaultValue != null)
                        valueString = String.valueOf(defaultValue);
                    else
                        throw mre;
                }
                if(returnType.isPrimitive()) {
                    if(returnType.getSimpleName().equals("int"))
                        initializer = CtField.Initializer.constant(Integer.parseInt(valueString));
                    else if(returnType.getSimpleName().equals("float"))
                        initializer = CtField.Initializer.byExpr(Float.parseFloat(valueString)+"f");
                    else if(returnType.getSimpleName().equals("double"))
                        initializer = CtField.Initializer.constant(Double.parseDouble(valueString));
                    else if(returnType.getSimpleName().equals("boolean"))
                        initializer = CtField.Initializer.byExpr(String.valueOf(Boolean.parseBoolean(valueString)));
                    else throw new IllegalStateException(returnType+" is not a supported primitive return type of a constant in "+iface.getName());
                } else if(returnType.equals(stringClass)) {
                    initializer = CtField.Initializer.constant(valueString);
                } else if(returnType.getSimpleName().equals("Map")){
                    CtField keyField = new CtField(stringClass, method.getName().toUpperCase(), impl);
                    keyField.setModifiers(Modifier.FINAL|Modifier.STATIC);
                    impl.addField(keyField, CtField.Initializer.constant(methodKey));
                    initializer = CtField.Initializer.byExpr("getMap("+keyField.getName()+")");
                } else if(returnType.getSimpleName().equals("String[]")){
                    CtField keyField = new CtField(stringClass, method.getName().toUpperCase(), impl);
                    keyField.setModifiers(Modifier.FINAL|Modifier.STATIC);
                    impl.addField(keyField, CtField.Initializer.constant(methodKey));
                    initializer = CtField.Initializer.byExpr("getStringArray("+keyField.getName()+")");
                    returnType = classPool.get(String[].class.getName());
                } else throw new IllegalStateException(returnType+" is not a supported return type of a constant in "+iface.getName());
               
                CtField valueField = new CtField(returnType, method.getName(), impl);
                impl.addField(valueField, initializer);
                //sb.append("this."+valueField.getName()+" = get"+getter+"(\""+methodKey+"\"));\n");
                methodImpl.setModifiers(Modifier.PUBLIC);
                methodImpl.setBody("return this."+valueField.getName()+";");
                impl.addMethod(methodImpl);
            }
            CtConstructor ctor = new CtConstructor(new CtClass[]{resourceBundle}, impl);
            ctor.setModifiers(Modifier.PUBLIC);
            ctor.setExceptionTypes(new CtClass[] {classPool.get(MissingResourceException.class.getName())});
            impl.addConstructor(ctor);
            ctor.setBody("super($1);");
           
            //System.out.println("impl name is "+impl.getName()+" iface name is "+ifaceName);
            Class<T> implClass = impl.toClass();
            T instance = implClass.getConstructor(ResourceBundle.class).newInstance(resBundle);
           
            cache.put(key, instance);
            return instance;
           
        } catch (NotFoundException e) {
            throw new Error(e);
        } catch (RuntimeException e) {
            throw new Error(e);
        } catch (CannotCompileException e) {
            throw new Error(e);
        } catch (InvocationTargetException e) {
            throw new Error(e);
        } catch (NoSuchMethodException e) {
            throw new Error(e);
        } catch (ClassNotFoundException e) {
            throw new Error(e);
        } catch (InstantiationException e) {
            throw new Error(e);
        } catch (IllegalAccessException e) {
            throw new Error(e);
        }
    }
}
TOP

Related Classes of com.habitsoft.kiyaa.server.ServerLocalization$ImplKey

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.