/** * Copyright (c) 2009 - 2011 AppWork UG(haftungsbeschränkt) <e-mail@appwork.org> * * This file is part of org.appwork.txtresource * * This software is licensed under the Artistic License 2.0, * see the LICENSE file or http://www.opensource.org/licenses/artistic-license-2.0.php * for details */ package org.appwork.txtresource; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.logging.Level; import org.appwork.storage.JSonStorage; import org.appwork.utils.Application; import org.appwork.utils.logging.Log; /** * @author thomas * */ public class TranslationHandler implements InvocationHandler { private final Class<? extends TranslateInterface> tInterface; private ArrayList<TranslateResource> lookup; private final HashMap<Method, String> cache; private final Method[] methods; private final HashMap<String, TranslateResource> resourceCache; public static final String DEFAULT = "en"; /** * @param class1 * @param lookup * @throws IOException */ public TranslationHandler(final Class<? extends TranslateInterface> class1, final String[] lookup) { this.tInterface = class1; this.methods = this.tInterface.getDeclaredMethods(); this.cache = new HashMap<Method, String>(); this.resourceCache = new HashMap<String, TranslateResource>(); this.lookup = this.fillLookup(lookup); } /** * @param m * @param types * @return */ private boolean checkTypes(final Method m, final Class<?>[] types) { final Class<?>[] parameters = m.getParameterTypes(); if (parameters.length != types.length) { return false; } if (types.length == 0) { return true; } for (int i = 0; i < types.length; i++) { if (types[i] != parameters[i]) { if (Number.class.isAssignableFrom(types[i])) { if (parameters[i] == int.class || parameters[i] == long.class || parameters[i] == double.class || parameters[i] == float.class || parameters[i] == byte.class || parameters[i] == char.class) { continue; } else { return false; } } else if (types[i] == Boolean.class && parameters[i] == boolean.class) { return true; } return false; } } return true; } /** * @param string * @param addComments * @return */ private String createFile(final String string, final boolean addComments) { final TranslateData map = new TranslateData(); this.cache.clear(); this.lookup = this.fillLookup(string); for (final Method m : this.tInterface.getDeclaredMethods()) { try { map.put(m.getName(), this.invoke(null, m, null).toString()); } catch (final Throwable e) { // TODO Auto-generated catch block e.printStackTrace(); } } String ret = JSonStorage.serializeToJson(map); if (addComments) { for (final Method m : this.tInterface.getDeclaredMethods()) { final Default def = m.getAnnotation(Default.class); final Description desc = m.getAnnotation(Description.class); String comment = ""; if (desc != null) { final String d = desc.value().replaceAll("[\\\r\\\n]+", "\r\n// "); comment += "\r\n// Description:\r\n// " + d; } if (def != null) { comment += "\r\n// Defaultvalue:\r\n// " + def.toString().replaceAll("[\\\r\\\n]+", "\r\n// "); } // if (comment.length() > 0) { ret = ret.replace("\"" + m.getName() + "\" : \"", comment + "\r\n\r\n " + "\"" + m.getName() + "\" : \""); } } } return ret; } /** * @param string * @return * @throws IOException */ private TranslateResource createTranslationResource(final String string) throws IOException { TranslateResource ret = this.resourceCache.get(string); if (ret != null) { return ret; } final String path = this.tInterface.getName().replace(".", "/") + "." + string + ".lng"; final URL url = Application.getRessourceURL(path, false); miss: if (url == null) { final Defaults ann = this.tInterface.getAnnotation(Defaults.class); if (ann != null) { for (final String d : ann.lngs()) { if (d.equals(string)) { // defaults Log.L.warning("Translation file missing:" + path + "Use Annotation Dev fallback"); break miss; } } } throw new NullPointerException("Missing Translation: " + path); } ret = new TranslateResource(url, string); this.resourceCache.put(string, ret); return ret; } /** * @param lookup2 * @return */ private ArrayList<TranslateResource> fillLookup(final String... lookup) { final ArrayList<TranslateResource> ret = new ArrayList<TranslateResource>(); TranslateResource res; boolean containsDefault = false; for (final String o : lookup) { try { if (TranslationHandler.DEFAULT.equals(o)) { containsDefault = true; } res = this.createTranslationResource(o); ret.add(res); } catch (final NullPointerException e) { Log.L.warning(e.getMessage()); } catch (final Throwable e) { Log.exception(Level.WARNING, e); } } if (!containsDefault) { try { res = this.createTranslationResource(TranslationHandler.DEFAULT); ret.add(res); } catch (final Throwable e) { Log.exception(Level.WARNING, e); } } return ret; } /** * @param ret * @param args * @return */ private String format(String ret, final Object[] args) { if (args != null) { int i = 0; for (final Object o : args) { i++; ret = ret.replace("%s" + i, o == null ? "null" : o.toString()); } } return ret; } public String getValue(final Method method, final ArrayList<TranslateResource> lookup) { String ret = null; TranslateResource res; for (final Iterator<TranslateResource> it = lookup.iterator(); it.hasNext();) { res = it.next(); try { ret = res.getEntry(method); if (ret != null) { return ret; } } catch (final Throwable e) { Log.L.warning("Exception in translation: " + this.tInterface.getName() + "." + res.getName()); Log.exception(Level.WARNING, e); it.remove(); } } if (ret == null) { ret = this.tInterface.getSimpleName() + "." + method.getName().substring(3); } return ret; } /* * (non-Javadoc) * * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, * java.lang.reflect.Method, java.lang.Object[]) */ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { final ArrayList<TranslateResource> lookup = this.lookup; // for speed reasons let all controller methods (@see // TRanslationINterface.java) start with _ if (method.getName().startsWith("_")) { if (method.getName().equals("_createFile")) { return this.createFile(args[0] + "", (Boolean) args[1]); } if (method.getName().equals("_getSupportedLanguages")) { return TranslationFactory.findTranslations(this.tInterface); } if (method.getName().equals("_setLanguage")) { this.cache.clear(); this.resourceCache.clear(); this.lookup = this.fillLookup(args[0] + ""); return null; } if (method.getName().equals("_getTranslation")) { final String methodname = args[1] + ""; final Object[] params = (Object[]) args[2]; final Class<?>[] types = new Class<?>[params.length]; for (int i = 0; i < params.length; i++) { types[i] = params[i].getClass(); } for (final Method m : this.methods) { if (m.getName().equals(methodname)) { if (this.checkTypes(m, types)) { final String ret = this.getValue(m, this.fillLookup(args[0] + "")); return this.format(ret, params); } } } } } String ret = this.cache.get(method); if (ret == null) { ret = this.getValue(method, lookup); if (ret != null) { this.cache.put(method, ret); } } return this.format(ret, args); } }