package com.spun.util.velocity;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import org.apache.velocity.runtime.RuntimeLogger;
import org.apache.velocity.runtime.parser.node.AbstractExecutor;
import org.apache.velocity.runtime.parser.node.BooleanPropertyExecutor;
import org.apache.velocity.runtime.parser.node.GetExecutor;
import org.apache.velocity.runtime.parser.node.PropertyExecutor;
import org.apache.velocity.util.ArrayIterator;
import org.apache.velocity.util.EnumerationIterator;
import org.apache.velocity.util.introspection.Info;
import org.apache.velocity.util.introspection.Introspector;
import org.apache.velocity.util.introspection.IntrospectorBase;
import org.apache.velocity.util.introspection.Uberspect;
import org.apache.velocity.util.introspection.UberspectLoggable;
import org.apache.velocity.util.introspection.VelMethod;
import org.apache.velocity.util.introspection.VelPropertyGet;
import org.apache.velocity.util.introspection.VelPropertySet;
import com.spun.util.ObjectUtils;
/**
* Implementation of Uberspect to provide the default introspective
* functionality of Velocity
*
* @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
* @version $Id: UberspectImpl.java,v 1.2.4.1 2004/03/03 23:23:08 geirm Exp $
*/
public class TestableUberspect implements Uberspect, UberspectLoggable
{
/**
* the default Velocity introspector
*/
private static IntrospectorBase introspector;
private static Introspector introspectorWithLog;
private RuntimeLogger log;
private static boolean beKindToNulls = false;
/***********************************************************************/
/**
* init - does nothing - we need to have setRuntimeLogger
* called before getting our introspector, as the default
* vel introspector depends upon it.;
*/
public void init() throws Exception
{
}
/***********************************************************************/
public void setRuntimeLogger(RuntimeLogger runtimeLogger)
{
introspector = new IntrospectorBase();
introspectorWithLog = new Introspector(runtimeLogger);
log = runtimeLogger;
}
public void setBeKindToNulls(boolean behavior)
{
beKindToNulls = behavior;
}
/***********************************************************************/
public Iterator getIterator(Object obj, Info i) throws Exception
{
return getStandardIterator(obj, i);
}
/***********************************************************************/
public static Iterator getStandardIterator(Object obj, Info i)
{
if (obj.getClass().isArray())
{
return new ArrayIterator(obj);
}
else if (obj instanceof Collection)
{
return ((Collection) obj).iterator();
}
else if (obj instanceof Map)
{
return ((Map) obj).values().iterator();
}
else if (obj instanceof Iterator)
{
return ((Iterator) obj);
}
else if (obj instanceof Enumeration) { return new EnumerationIterator((Enumeration) obj); }
throw new VelocityParsingError("Could not determine type of iterator in " + "#foreach loop ", i);
}
/***********************************************************************/
public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i) throws Exception
{
if (obj == null)
{
if(beKindToNulls)
{
return null;
}
else
{
throw new VelocityParsingError("tried " + getMethodText("null", methodName, args), i);
}
}
Method m = introspector.getMethod(obj.getClass(), methodName, args);
if (m == null) { throw new VelocityParsingError("Method " + getMethodText(obj.getClass().getName(), methodName, args) + " does not exist.", i); }
return new VelMethodImpl(m);
}
/***********************************************************************/
public static String getMethodText(String className, String methodName, Object[] args)
{
StringBuffer methodSignature = new StringBuffer();
for (int i = 0; args != null && i < args.length; i++)
{
methodSignature.append(ObjectUtils.getClassName(args[i]));
methodSignature.append(i == (args.length - 1) ? "" : ", ");
}
return className + "." + methodName + "(" + methodSignature + ") ";
}
/***********************************************************************/
public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) throws Exception
{
AbstractExecutor executor;
if (obj == null) { throw new VelocityParsingError("tried " + getPropertyText("null", identifier), i); }
Class<? extends Object> claz = obj.getClass();
// trying getFoo()
executor = new PropertyExecutor(log, introspectorWithLog, claz, identifier);
if (!executor.isAlive())
{
// trying get("foo")
executor = new GetExecutor(log, introspectorWithLog, claz, identifier);
}
if (!executor.isAlive())
{
// trying isFoo()
executor = new BooleanPropertyExecutor(log, introspectorWithLog, claz, identifier);
}
if (!executor.isAlive()) { throw new VelocityParsingError("Did not find " + getPropertyText(obj.getClass().getName(), identifier), i); }
return new VelGetterImpl(executor);
}
/***********************************************************************/
private String getPropertyText(String className, String identifier)
{
return className + "." + identifier + " ";
}
/***********************************************************************/
public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i) throws Exception
{
Class<? extends Object> claz = obj.getClass();
VelMethod vm = null;
try
{
/*
* first, we introspect for the set<identifier> setter method
*/
Object[] params = {arg};
try
{
vm = getMethod(obj, "set" + identifier, params, i);
if (vm == null) { throw new NoSuchMethodException(); }
}
catch (NoSuchMethodException nsme2)
{
StringBuffer sb = new StringBuffer("set");
sb.append(identifier);
if (Character.isLowerCase(sb.charAt(3)))
{
sb.setCharAt(3, Character.toUpperCase(sb.charAt(3)));
}
else
{
sb.setCharAt(3, Character.toLowerCase(sb.charAt(3)));
}
vm = getMethod(obj, sb.toString(), params, i);
if (vm == null) { throw new NoSuchMethodException(); }
}
}
catch (NoSuchMethodException nsme)
{
/*
* right now, we only support the Map interface
*/
if (Map.class.isAssignableFrom(claz))
{
Object[] params = {new Object(), new Object()};
vm = getMethod(obj, "put", params, i);
if (vm != null)
return new VelSetterImpl(vm, identifier);
}
}
return (vm != null) ? new VelSetterImpl(vm) : null;
}
/***********************************************************************/
/* INNER CLASS */
/***********************************************************************/
public static class VelMethodImpl implements VelMethod
{
Method method = null;
public VelMethodImpl(Method m)
{
method = m;
}
public Object invoke(Object o, Object[] params) throws Exception
{
return method.invoke(o, params);
}
public boolean isCacheable()
{
return true;
}
public String getMethodName()
{
return method.getName();
}
public Class getReturnType()
{
return method.getReturnType();
}
}
public static class VelGetterImpl implements VelPropertyGet
{
AbstractExecutor ae = null;
public VelGetterImpl(AbstractExecutor exec)
{
ae = exec;
}
public Object invoke(Object o) throws Exception
{
return ae.execute(o);
}
public boolean isCacheable()
{
return true;
}
public String getMethodName()
{
return ae.getMethod().getName();
}
}
public static class VelSetterImpl implements VelPropertySet
{
VelMethod vm = null;
String putKey = null;
public VelSetterImpl(VelMethod velmethod)
{
this.vm = velmethod;
}
public VelSetterImpl(VelMethod velmethod, String key)
{
this.vm = velmethod;
putKey = key;
}
public Object invoke(Object o, Object value) throws Exception
{
ArrayList<Object> al = new ArrayList<Object>();
if (putKey != null)
{
al.add(putKey);
al.add(value);
}
else
{
al.add(value);
}
return vm.invoke(o, al.toArray());
}
public boolean isCacheable()
{
return true;
}
public String getMethodName()
{
return vm.getMethodName();
}
}
}