
package org.onemind.commons.java.pattern;

import java.lang.reflect.Method;
import org.onemind.commons.java.datastructure.InheritableValueMap;
import org.onemind.commons.java.lang.reflect.ReflectUtils;
/**
 * An abstract implementation of visitor that is extensible for handling 
 * different kind of object nodes by simple adding more methods. The subclass need to
 * set up for handlers of node type in the constructor.
 * 
 * @author TiongHiang Lee (thlee@onemindsoft.org)
 * @version $Id: DynamicVisitor.java,v 1.2 2004/10/31 16:02:08 thlee Exp $ $Name:  $
 */
public abstract class DynamicVisitor
{

    /** the object array class **/
    private static Class OBJECT_ARRAY_CLASS = new Object[0].getClass();

    /**
     * The handler
     * @author TiongHiang Lee (thlee@onemindsoft.org)
     * @version $Id: DynamicVisitor.java,v 1.2 2004/10/31 16:02:08 thlee Exp $ $Name:  $
     */
    public static interface NodeHandler
    {

        /**
         * Handle node 
         * @param node
         * @param data
         */
        public Object handleNode(Object node, Object[] data) throws Exception;
    }

    /**
     * A handler use reflection to invoke given method for visiting
     */
    protected class MethodNodeHandler implements NodeHandler
    {

        /** the method **/
        private Method _method;

        /**
         * Constructor
         * @param methodName the method name
         */
        public MethodNodeHandler(Method method)
        {
            _method = method;
        }

        /** 
         * {@inheritDoc}
         */
        public Object handleNode(Object node, Object[] data) throws Exception
        {
            Object[] args = {node, data};
            return _method.invoke(DynamicVisitor.this, args);
        }
    }

    /** contains the handlers for different kind of nodese **/
    private final InheritableValueMap _handlers = new InheritableValueMap();

    /**
     * Constructor
     */
    public DynamicVisitor()
    {
        initNodeHandlers();
    }
    
    /**
     * Initialize the node handlers
     */
    protected abstract void initNodeHandlers();

    /**
     * Add node handler
     * @param type the type
     * @param handler the handler
     */
    protected void addNodeHandler(Class type, NodeHandler handler)
    {
        _handlers.put(type, handler);
    }

    /**
     * Add MethodNodeHandler using the given method name
     * throws RuntimeException if the method cannot be found.
     * @param type the type
     * @param methodName the method name
     */
    protected void addMethodNodeHandler(Class type, String methodName)
    {
        try
        {
            Class args[] = {type, new Object[0].getClass()};
            Method m = ReflectUtils.getMethod(getClass(), methodName, args);
            _handlers.put(type, new MethodNodeHandler(m));
        } catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }

    /**
     * The object
     * @param obj the object
     * @param args the arguments
     */
    public Object visit(Object obj, Object[] args) throws Exception
    {
        NodeHandler handler = (NodeHandler) _handlers.resolve(obj.getClass());
        if (handler != null)
        {
            return handler.handleNode(obj, args);
        } else
        {
            throw new IllegalArgumentException("Cannot find handler method for object " + obj);
        }
    }
}