/*
* Copyright 2013 Andriy Vityuk
*
* 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 com.vityuk.ginger.proxy;
import com.vityuk.ginger.InvalidParameterTypeException;
import com.vityuk.ginger.InvalidReturnTypeException;
import com.vityuk.ginger.Localizable;
import com.vityuk.ginger.PluralCount;
import com.vityuk.ginger.Select;
import com.vityuk.ginger.provider.LocalizationProvider;
import com.vityuk.ginger.util.GingerUtils;
import com.vityuk.ginger.util.MiscUtils;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
public class ReflectProxyBuilder implements ProxyBuilder {
@Override
public <T> T createProxy(Class<T> object, LocalizationProvider localizationProvider) {
Localizable localizable = (Localizable) Proxy.newProxyInstance(object.getClassLoader(),
new Class[]{object},
new MyInvocationHandler(localizationProvider));
return object.cast(localizable);
}
private class MyInvocationHandler implements InvocationHandler {
private final LocalizationProvider localizationProvider;
public MyInvocationHandler(LocalizationProvider localizationProvider) {
this.localizationProvider = localizationProvider;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String key = GingerUtils.createKeyFromMethod(method);
Class<?> type = method.getReturnType();
if (GingerUtils.isConstantMethod(method)) {
return getConstant(localizationProvider, method, type, key);
} else {
return getMessage(localizationProvider, method, type, key, args);
}
}
private Object getConstant(LocalizationProvider localizationProvider, Method method, Class<?> type, String key) {
if (type == String.class) {
return localizationProvider.getString(key);
}
if (type == Boolean.class) {
return localizationProvider.getBoolean(key);
}
if (type == Integer.class) {
return localizationProvider.getInteger(key);
}
if (type == Long.class) {
return localizationProvider.getLong(key);
}
if (type == Float.class) {
return localizationProvider.getFloat(key);
}
if (type == Double.class) {
return localizationProvider.getDouble(key);
}
if (type == List.class) {
// TODO: generics support
return localizationProvider.getStringList(key);
}
if (type == Map.class) {
// TODO: generics support
return localizationProvider.getStringMap(key);
}
throw new InvalidReturnTypeException(type, method);
}
private Object getMessage(LocalizationProvider localizationProvider, Method method, Class<?> type, String key, Object[] args) {
if (type != String.class) {
throw new InvalidReturnTypeException(type, method);
}
int selectorParameterIndex = GingerUtils.indexOfParameterAnnotation(method, Select.class);
if (selectorParameterIndex != -1) {
return createSelectorMessageLookupCallback(localizationProvider, method, key, selectorParameterIndex, args);
}
int pluralCountParameterIndex = GingerUtils.indexOfParameterAnnotation(method, PluralCount.class);
if (pluralCountParameterIndex != -1) {
return createPluralMessageLookupCallback(localizationProvider, method, key, pluralCountParameterIndex, args);
}
return localizationProvider.getMessage(key, args);
}
private Object createSelectorMessageLookupCallback(LocalizationProvider localizationProvider, Method method, String key, int parameterIndex, Object[] args) {
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
if (parameterType.isPrimitive() || MiscUtils.isWrapperType(parameterType)) {
// TODO: consider more informative exception
throw new InvalidParameterTypeException(parameterType, method);
}
String selector = String.valueOf(args[parameterIndex]);
Object[] parameters = ArrayUtils.remove(args, parameterIndex);
return localizationProvider.getSelectedMessage(key, selector, parameters);
}
private Object createPluralMessageLookupCallback(LocalizationProvider localizationProvider, Method method, String key, int parameterIndex, Object[] args) {
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
if (!GingerUtils.isIntNumericType(parameterType)) {
// TODO: consider more informative exception
throw new InvalidParameterTypeException(parameterType, method);
}
Number pluralCount = (Number) args[parameterIndex];
Object[] parameters = ArrayUtils.remove(args, parameterIndex);
return localizationProvider.getPluralMessage(key, pluralCount, parameters);
}
}
}