Java tutorial
/* * Hermes - GWT Server-side I18N Library * Copyright (C) 2011 Matt Bertolini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package com.mattbertolini.hermes; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Date; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import com.google.gwt.i18n.client.LocalizableResource.Key; import com.google.gwt.i18n.client.Messages.AlternateMessage; import com.google.gwt.i18n.client.Messages.DefaultMessage; import com.google.gwt.i18n.client.Messages.PluralCount; import com.google.gwt.i18n.client.Messages.PluralText; import com.google.gwt.i18n.client.Messages.Select; import com.google.gwt.i18n.client.PluralRule; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.ibm.icu.text.MessageFormat; import com.ibm.icu.text.PluralRules; import com.ibm.icu.util.ULocale; /** * * @author Matt Bertolini */ public class MessagesProxy extends AbstractI18nProxy { private static final char OPEN_BRACKET = '['; private static final char CLOSE_BRACKET = ']'; private static final char SEPARATOR = '|'; private PluralRules pluralRules; public MessagesProxy(Class<?> clazz, ULocale locale, Properties properties) { super(clazz, locale, properties); this.pluralRules = PluralRules.forLocale(this.getLocale()); } @Override public Object parse(Method method, Object[] args) { return this.parseMessage(method, args); } private Object parseMessage(Method method, Object[] args) { String messageName; Key keyAnnotation = method.getAnnotation(Key.class); if (keyAnnotation == null) { messageName = method.getName(); } else { messageName = keyAnnotation.value(); } List<String> formsNames = new LinkedList<String>(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); Class<?>[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { Annotation[] annotations = parameterAnnotations[i]; Class<?> type = parameterTypes[i]; for (Annotation annotation : annotations) { if (PluralCount.class.isAssignableFrom(annotation.annotationType()) && (int.class.isAssignableFrom(type) || Integer.class.isAssignableFrom(type) || short.class.isAssignableFrom(type) || Short.class.isAssignableFrom(type))) { Number num = (Number) args[i]; Plural plural; // Check for a custom plural rule. PluralCount pc = (PluralCount) annotation; Class<? extends PluralRule> pluralRuleClass = pc.value(); if (!pluralRuleClass.isInterface() && PluralRule.class.isAssignableFrom(pluralRuleClass)) { PluralRule customRule = this.instantiateCustomPluralRuleClass(pluralRuleClass); plural = CustomPlural.fromNumber(customRule, num.intValue()); } else { plural = GwtPlural.fromNumber(this.pluralRules, num.doubleValue()); } formsNames.add(plural.getGwtValue()); } else if (Select.class.isAssignableFrom(annotation.annotationType())) { if (Enum.class.isAssignableFrom(type)) { Enum<?> enumConstant = (Enum<?>) args[i]; String name; if (enumConstant == null) { name = "other"; } else { name = enumConstant.name(); } formsNames.add(name); } else if (String.class.isAssignableFrom(type)) { String str = (String) args[i]; if (str == null) { str = "other"; } formsNames.add(str); } else if (type.isPrimitive() && (int.class.isAssignableFrom(type) || long.class.isAssignableFrom(type) || float.class.isAssignableFrom(type) || short.class.isAssignableFrom(type) || double.class.isAssignableFrom(type))) { Number num = (Number) args[i]; formsNames.add(num.toString()); } } } } String patternName = this.buildPatternName(messageName, formsNames); String pattern = this.getProperties().getProperty(patternName); if (pattern == null) { Map<String, String> altMsgMap = this.buildAlternateMessageMap(messageName, method); pattern = altMsgMap.get(patternName); if (pattern == null) { pattern = this.getProperties().getProperty(messageName); if (pattern == null) { DefaultMessage defaultMessage = method.getAnnotation(DefaultMessage.class); if (defaultMessage != null) { pattern = defaultMessage.value(); } else { throw new RuntimeException("No message found for key " + messageName); } } } } MessageFormat formatter = new MessageFormat(pattern, this.getLocale()); Object retVal; if (method.getReturnType().equals(SafeHtml.class)) { Object[] safeArgs = null; if (args != null) { safeArgs = new Object[args.length]; for (int i = 0; i < args.length; i++) { Object arg = args[i]; Class<?> argType = parameterTypes[i]; if (SafeHtml.class.isAssignableFrom(argType)) { SafeHtml sh = (SafeHtml) arg; safeArgs[i] = sh.asString(); } else if (Number.class.isAssignableFrom(argType) || Date.class.isAssignableFrom(argType)) { // Because of the subformat pattern of dates and // numbers, we cannot escape them. safeArgs[i] = arg; } else { safeArgs[i] = SafeHtmlUtils.htmlEscape(arg.toString()); } } } String formattedString = formatter.format(safeArgs, new StringBuffer(), null).toString(); // Would rather use fromSafeConstant() but doesn't work on server. retVal = SafeHtmlUtils.fromTrustedString(formattedString); } else { retVal = formatter.format(args, new StringBuffer(), null).toString(); } return retVal; } /** * Instantiates the plural rule class given inside the PluralCount * annotation. The pluralRule class that is instantiated is based on the * language originally given to the library. If that language is not found, * defaults to the given class. * * @param clazz The PluralRule class * @return An instantiated PluralRule class */ private PluralRule instantiateCustomPluralRuleClass(Class<? extends PluralRule> clazz) { PluralRule retVal = null; try { String pluralClassName = clazz.getName() + "_" + this.getLocale().getLanguage(); try { Class<?> pClass = Class.forName(pluralClassName); retVal = (PluralRule) pClass.newInstance(); } catch (ClassNotFoundException e) { retVal = clazz.newInstance(); } } catch (InstantiationException e) { throw new RuntimeException("Could not instantiate custom PluralRule class. " + "Make sure the class is not an abstract class or interface and has a default constructor.", e); } catch (IllegalAccessException e) { throw new RuntimeException("Could not instantiate custom Plural Rule class.", e); } return retVal; } private String buildPatternName(String baseName, List<String> pluralNames) { String retVal; StringBuilder pluralStr = new StringBuilder(); boolean defaultMessage = true; if (pluralNames != null && !pluralNames.isEmpty()) { pluralStr.append(OPEN_BRACKET); boolean first = true; for (String name : pluralNames) { if (!name.equals(GwtPlural.OTHER.getGwtValue())) { defaultMessage = false; } if (first) { first = false; } else { pluralStr.append(SEPARATOR); } pluralStr.append(name); } pluralStr.append(CLOSE_BRACKET); } if (defaultMessage) { retVal = baseName; } else { retVal = baseName + pluralStr.toString(); } return retVal; } private Map<String, String> buildAlternateMessageMap(String baseName, Method method) { Map<String, String> retMap = new LinkedHashMap<String, String>(); AlternateMessage altMsgAnnotation = method.getAnnotation(AlternateMessage.class); PluralText pluralTextAnnotation = method.getAnnotation(PluralText.class); String[] values = new String[0]; if (altMsgAnnotation != null) { values = altMsgAnnotation.value(); } else if (pluralTextAnnotation != null) { // Fall back on the PluralText if no AlternateMessage found. values = pluralTextAnnotation.value(); } for (int i = 0; i < values.length; i += 2) { retMap.put(baseName + OPEN_BRACKET + values[i] + CLOSE_BRACKET, values[i + 1]); } return retMap; } }