package wicket.contrib.groovy.builder;
import groovy.lang.Closure;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConstructorUtils;
import org.apache.commons.beanutils.PropertyUtils;
import wicket.Component;
import wicket.contrib.groovy.builder.util.AttributeUtils;
public abstract class BuilderSupport
{
private Class targetClass;
public BuilderSupport(Class targetClass) {
this.targetClass = targetClass;
}
public Class getTargetClass()
{
return targetClass;
}
static DynamicJavaWrapper wrapper;
/**
* This avoids the compilation issue with java calling groovy. Normally we'd not want
* to work this hard, but we're getting maven issues.
* @return
*/
public static DynamicJavaWrapper getDynamicJavaWrapper()
{
if(wrapper == null)
{
try
{
wrapper = (DynamicJavaWrapper) Class.forName("wicket.contrib.groovy.builder.DynamicJavaWrapperUtil").newInstance();
}
catch (Exception e)
{
throw new WicketComponentBuilderException("Can't get wrapper instanc");
}
}
return wrapper;
}
protected Object generateInstance(Map attributes, List constructorParameters)throws Exception
{
// Check attributes and see if anything is a closure. Will require sub-class
List closures = new ArrayList();
List methods = new ArrayList();
if(attributes != null)
{
Iterator attrIterator = attributes.keySet().iterator();
while(attrIterator.hasNext())
{
String attrName = (String) attrIterator.next();
if(attributes.get(attrName) instanceof Closure)
{
Closure methodOverride = (Closure) attributes.get(attrName);
attrIterator.remove();
closures.add(methodOverride);
methods.add(matchClosuresToMethods(getTargetClass(), attrName, methodOverride));
}
}
}
Class localComponentClass;
String extraCode = injectExtraCode();
if(closures.size() > 0 || extraCode != null)
{
localComponentClass = getDynamicJavaWrapper().wrapClass(getTargetClass(), methods, injectExtraCode(), injectInterfaces());
}
else
{
localComponentClass = getTargetClass();
}
Object generated = ConstructorUtils.invokeConstructor(localComponentClass, constructorParameters.toArray());
if(generated instanceof DynamicJavaWrapperScriptable)
{
DynamicJavaWrapperScriptable scriptable = (DynamicJavaWrapperScriptable) generated;
getDynamicJavaWrapper().fillMethods(scriptable, methods, closures);
}
return generated;
}
/**
* Override if you want to inject extra code. This is clunky, and will probably go away. Just for
* ListView right now.
*
* @return
*/
protected String injectExtraCode()
{
return null;
}
/**
* Override if you want to inject interfaces. This is clunky, and will probably go away. Just for
* ListView right now.
*
* @return
*/
protected String injectInterfaces()
{
return null;
}
public static Method matchClosuresToMethods(Class componentClass, String methodName, Closure closure)
{
//TODO: do a better type matchup. Leave this alone for now. Require strict types.
try
{
Method method = null;
while (componentClass.equals(Object.class) == false && method == null)
{
Method[] methods = componentClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++)
{
Method checkMethod = methods[i];
if (checkMethod.getName().equals(methodName))
{
if ((closure.getParameterTypes().length == 1 && checkMethod.getParameterTypes().length <= 1)||
closure.getParameterTypes().length == checkMethod.getParameterTypes().length)
{
method = checkMethod;
break;
}
}
}
componentClass = componentClass.getSuperclass();
}
if(method == null)
throw new WicketComponentBuilderException("Can't get method for closure parameter types");
return method;
}
catch (SecurityException e)
{
throw new WicketComponentBuilderException("Can't get method for closure parameter types", e);
}
}
/**
* Tries to match up all properties. Any extra properties will cause it to fail.
*
* @param component
* @param attrs
*/
public static void setOtherProperties(Object target, Map attrs)
{
if(attrs == null || attrs.size() == 0)
return;
Iterator keyIter = attrs.keySet().iterator();
//This is s a little dirty
List keyStore = new ArrayList();
while(keyIter.hasNext())
{
String key = (String) keyIter.next();
Object value = attrs.get(key);
keyStore.add(key);
PropertyDescriptor propertyDescriptor;
try
{
propertyDescriptor = PropertyUtils.getPropertyDescriptor(target, key);
}
catch (Exception e)
{
throw new WicketComponentBuilderException("Error with property for '"+ key +"'", e);
}
if(propertyDescriptor == null)
{
throw new WicketComponentBuilderException("No property for '"+ key +"'");
}
else
{
// if(propertyDescriptor.getPropertyType().isPrimitive())
// value = value.toString();
//
value = AttributeUtils.generalAttributeConversion(propertyDescriptor.getPropertyType(), value);
try
{
//Running pretty fast and loose here. Good luck.
BeanUtils.setProperty(target, key, value);
}
catch (Exception e)
{
throw new WicketComponentBuilderException("Error setting property for '"+ key +"'", e);
}
}
}
for(int i=0; i<keyStore.size(); i++)
{
attrs.remove(keyStore.get(i));
}
}
}