Java tutorial
/* * Copyright 2013 Christian Robert * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.perdian.commons.i18n.polyglot; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Optional; import org.springframework.context.MessageSource; import de.perdian.commons.i18n.polyglot.annotations.PolyglotConfiguration; import de.perdian.commons.i18n.polyglot.annotations.PolyglotDefault; import de.perdian.commons.i18n.polyglot.annotations.PolyglotKey; class PolyglotImpl<T> implements Polyglot<T> { private final Map<Locale, T> localeToInstanceCache = new HashMap<>(); private Class<T> instanceClass = null; private MessageSource messageSource = null; @Override public T getInstance(Locale locale) { if (locale == null) { throw new IllegalArgumentException("Parameter 'locale' must not be null"); } else { T cachedInstance = this.getLocaleToInstanceCache().get(locale); if (cachedInstance == null) { Class<T> instanceClass = this.getInstanceClass(); cachedInstance = instanceClass.cast(Proxy.newProxyInstance(instanceClass.getClassLoader(), new Class[] { instanceClass }, this.createInvocationHandler(locale))); this.getLocaleToInstanceCache().put(locale, cachedInstance); } return cachedInstance; } } private InvocationHandler createInvocationHandler(Locale locale) { PolyglotInvocationHandler invocationHandler = new PolyglotInvocationHandler(); invocationHandler.setLocale(locale); invocationHandler.setMessageSource(this.getMessageSource()); invocationHandler.setInfoMap(this.createInvocationInfoMap()); return invocationHandler; } private Map<Method, PolyglotInvocationInfo> createInvocationInfoMap() { PolyglotConfiguration configuration = this.getInstanceClass().getAnnotation(PolyglotConfiguration.class); PolyglotKeyGenerator keyGenerator = new PolyglotKeyGenerator(); keyGenerator .setPrefix(Optional.ofNullable(configuration).map(PolyglotConfiguration::keyPrefix).orElse(null)); keyGenerator .setPostfix(Optional.ofNullable(configuration).map(PolyglotConfiguration::keyPostfix).orElse(null)); Map<Method, PolyglotInvocationInfo> resultMap = new HashMap<>(); this.appendInvocationInfos(resultMap, this.getInstanceClass(), keyGenerator); return resultMap; } private void appendInvocationInfos(Map<Method, PolyglotInvocationInfo> resultMap, Class<?> interfaceClass, PolyglotKeyGenerator keyGenerator) { Class<?>[] implementedInterfaces = interfaceClass.getInterfaces(); if (implementedInterfaces != null) { for (Class<?> implementedInterface : implementedInterfaces) { this.appendInvocationInfos(resultMap, implementedInterface, keyGenerator); } } StringBuilder exceptionCollectionString = new StringBuilder(); Method[] interfaceMethods = interfaceClass.getMethods(); if (interfaceMethods != null) { for (Method interfaceMethod : interfaceMethods) { try { resultMap.put(interfaceMethod, this.createInvocationInfo(interfaceMethod, keyGenerator)); } catch (Exception e) { exceptionCollectionString.append("\n- ").append(interfaceMethod.getName()); exceptionCollectionString.append(": ").append(e.getMessage()); } } if (exceptionCollectionString.length() > 0) { StringBuilder exceptionString = new StringBuilder(); exceptionString.append("Analyzation of polyglot information for class '").append(interfaceClass) .append("' "); exceptionString.append("failed:").append(exceptionCollectionString); throw new IllegalArgumentException(exceptionString.toString()); } } } private PolyglotInvocationInfo createInvocationInfo(Method interfaceMethod, PolyglotKeyGenerator keyGenerator) { Class<?> returnType = interfaceMethod.getReturnType(); if (!returnType.isAssignableFrom(String.class)) { throw new IllegalArgumentException("Return type '" + returnType.getName() + "' is not compatible to result type '" + String.class.getName() + "'"); } else { PolyglotKey polyglotKey = interfaceMethod.getAnnotation(PolyglotKey.class); String interfaceKey = polyglotKey == null ? interfaceMethod.getName() : polyglotKey.value(); PolyglotDefault polyglotDefault = interfaceMethod.getAnnotation(PolyglotDefault.class); PolyglotInvocationInfo invocationInfo = new PolyglotInvocationInfo(); invocationInfo.setDefaultValue(polyglotDefault == null ? null : polyglotDefault.value()); invocationInfo.setKey(keyGenerator.generateKey(interfaceKey)); return invocationInfo; } } // --------------------------------------------------------------------------- // --- Meta creation // --------------------------------------------------------- // --------------------------------------------------------------------------- @Override public PolyglotMeta getMeta(Locale locale) { if (locale == null) { throw new IllegalArgumentException("Parameter 'locale' must not be null"); } else { PolyglotMeta metaImpl = new PolyglotMeta(locale); Map<Method, PolyglotInvocationInfo> invocationMap = this.createInvocationInfoMap(); for (Map.Entry<Method, PolyglotInvocationInfo> invocationEntry : invocationMap.entrySet()) { PolyglotInvocationInfo invocationInfo = invocationEntry.getValue(); String localizedValue = this.getMessageSource().getMessage(invocationInfo.getKey(), null, invocationInfo.getDefaultValue(), locale); if (localizedValue == null || localizedValue.length() <= 0) { metaImpl.addMissingValueKey(invocationInfo.getKey()); } metaImpl.addKey(invocationInfo.getKey()); } return metaImpl; } } // ------------------------------------------------------------------------- // --- Inner classes ------------------------------------------------------- // ------------------------------------------------------------------------- static class PolyglotKeyGenerator { private String prefix = null; private String postfix = null; String generateKey(String key) { StringBuilder result = new StringBuilder(); result.append(Optional.ofNullable(this.getPrefix()).orElse("")); result.append(key); result.append(Optional.ofNullable(this.getPostfix()).orElse("")); return result.toString(); } String getPrefix() { return this.prefix; } void setPrefix(String prefix) { this.prefix = prefix; } String getPostfix() { return this.postfix; } void setPostfix(String postfix) { this.postfix = postfix; } } // ------------------------------------------------------------------------- // --- Property access methods --------------------------------------------- // ------------------------------------------------------------------------- Map<Locale, T> getLocaleToInstanceCache() { return this.localeToInstanceCache; } Class<T> getInstanceClass() { return this.instanceClass; } void setInstanceClass(Class<T> instanceClass) { this.instanceClass = instanceClass; } MessageSource getMessageSource() { return this.messageSource; } void setMessageSource(MessageSource messageSource) { this.messageSource = messageSource; } }