/*
* Copyright 2014 cruxframework.org.
*
* 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 org.cruxframework.crux.core.config;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.Map;
import java.util.PropertyResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import org.cruxframework.crux.core.i18n.DefaultServerMessage;
import org.cruxframework.crux.core.i18n.MessageException;
import com.google.gwt.i18n.client.Messages.DefaultMessage;
/**
* Dynamic proxy for message resources.
* @author Thiago da Rosa de Bustamante
* @author Gesse S. F. Dafe
*/
public abstract class ConstantsInvocationHandler implements InvocationHandler
{
private Class<?> targetInterface;
private Map<String, String> resolvedConstants = new ConcurrentHashMap<String, String>();
private boolean isCacheable = true;
/**
*
* @param targetInterface
*/
public ConstantsInvocationHandler(Class<?> targetInterface, boolean isCacheable)
{
this.targetInterface = targetInterface;
this.isCacheable = isCacheable;
}
/**
*
* @param targetInterface
*/
public ConstantsInvocationHandler(Class<?> targetInterface)
{
this(targetInterface, true);
}
/**
*
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
String name = method.getName();
if (this.isCacheable && resolvedConstants.containsKey(name))
{
return resolvedConstants.get(name);
}
String message = null;
try
{
if (isValidPropertySetter(method))
{
invokeSetter(method, args);
}
else
{
message = getMessageFromProperties(args, name);
if (message == null)
{
message = getMessageFromAnnotation(method, args, name);
}
}
}
catch (Throwable e)
{
message = getMessageFromAnnotation(method, args, name);
}
return message;
}
/**
* @param args
* @param name
* @return
*/
protected String getMessageFromProperties(Object[] args, String name)
{
PropertyResourceBundle properties = getPropertiesForLocale(targetInterface);
String message = null;
if (properties != null)
{
message = MessageFormat.format(properties.getString(name),args);
if (this.isCacheable)
{
resolvedConstants.put(name, message);
}
}
return message;
}
/**
* @param method
* @param args
* @param name
* @return
*/
protected String getMessageFromAnnotation(Method method, Object[] args, String name)
{
DefaultServerMessage serverAnnot = method.getAnnotation(DefaultServerMessage.class);
DefaultMessage clientAnnot = method.getAnnotation(DefaultMessage.class);
String value = null;
if (serverAnnot != null)
{
value = MessageFormat.format(serverAnnot.value(),args);
} else if(clientAnnot != null)
{
value = MessageFormat.format(clientAnnot.value(),args);
}
if (value != null)
{
if (this.isCacheable)
{
resolvedConstants.put(name, value);
}
return value;
}
return null;
}
/**
*
* @param method
* @return
*/
protected boolean isValidPropertySetter(Method method)
{
String methodName = method.getName();
if (methodName.startsWith("set") && methodName.length() > 3)
{
String property = getPropertyFromSetterMethodName(methodName);
if (this.isCacheable && resolvedConstants.containsKey(property))
{
return method.getParameterTypes().length == 1;
}
try
{
targetInterface.getMethod(property, new Class[]{});
return true;
}
catch (Throwable e)
{
return false;
}
}
return false;
}
/**
*
* @param properties
* @param method
* @param args
*/
protected void invokeSetter(Method method, Object[] args)
{
if (this.isCacheable)
{
String property = getPropertyFromSetterMethodName(method.getName());
Object value = args[0];
if (value == null)
{
resolvedConstants.remove(property);
}
else
{
resolvedConstants.put(property, value.toString());
}
}
}
/**
*
* @param targetInterface
* @param locale
* @return
*/
protected static PropertyResourceBundle loadProperties (Class<?> targetInterface, final Locale locale)
{
PropertyResourceBundle properties = null;
try
{
properties = (PropertyResourceBundle) PropertyResourceBundle.getBundle(targetInterface.getSimpleName(), locale);
}
catch (Exception e)
{
try
{
properties = (PropertyResourceBundle) PropertyResourceBundle.getBundle(targetInterface.getCanonicalName(), locale);
}
catch (Exception f)
{
try
{
String resourceName = "/"+targetInterface.getName().replaceAll("\\.", "/") + ".properties";
InputStream input = targetInterface.getClassLoader().getResourceAsStream(resourceName);
if (input != null)
{
properties = new PropertyResourceBundle(input);
}
}
catch (IOException e1)
{
throw new MessageException(e.getMessage(), e);
}
}
}
return properties;
}
/**
*
* @param methodName
* @return
*/
private String getPropertyFromSetterMethodName(String methodName)
{
String property = methodName.substring(3);
if (property.length() == 1)
{
property = Character.toLowerCase(property.charAt(0))+"";
}
else
{
property = Character.toLowerCase(property.charAt(0)) + property.substring(1);
}
return property;
}
/**
*
* @param <T>
* @param targetInterface
* @return
*/
protected abstract <T> PropertyResourceBundle getPropertiesForLocale(final Class<T> targetInterface);
}