/* 
 * E-XML Library:  For XML, XML-RPC, HTTP, and related.
 * Copyright (C) 2002-2008  Elias Ross
 * 
 * genman@noderunner.net
 * http://noderunner.net/~genman
 * 
 * 1025 NE 73RD ST
 * SEATTLE WA 98115
 * USA
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * $Id$
 */

package net.noderunner.xmlrpc;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * This class uses Java Reflection to call methods matching an Xml-Rpc call.
 * This class uses the Adapter pattern.
 *
 * @author Elias Ross
 * @version 1.0
 */
public class Invoker 
	implements XmlRpcHandler 
{
	private Object target;
	private Class targetClass;

	/**
	 * For converting the method return objects into Xml-Rpc recognized types.
	public static interface OutputAdapter
		extends ParamIterator
	{
		void setParamIterator(ParamIterator i) throws InvokerException;
		boolean hasNext() throws InvokerException;
		Object next() throws InvokerException;
	}
	 */

	/**
	 * For converting the method return objects into Xml-Rpc recognized types.
	public static interface InputAdapter {
		void setParamIterator(ParamIterator i) throws InvokerException;
		boolean hasNext() throws InvokerException;
		Object next() throws InvokerException;
	}
	 */
		
	/**
	 * Construct an Invoker, wrapping an Object, adapting it for use
	 * with XmlRpcServer.
	 * @param target to invoke
	 * @see XmlRpcServer
	 */
	public Invoker(Object target) {
		this.target = target;
		targetClass = target.getClass();
	}

	/**
	 * Sets the optional input adapter, which will translate parameters
	 * from.
	public void setInputAdapter(InputAdapter a);
	 */

	/**
	 * This method copies the first level of parameters to a
	 * <code>List</code> for calling a named method on the wrapped Object.
	 * The advantages of streaming input are lost, unfortunately, to
	 * prepare the parameter list for execution.  The results of the
	 * invocation, if a <code>Collection</code> or <code>Iterator</code>
	 * object, are wrapped.
	 * 
	 * @throws InvokerException if the invocation call failed on the wrapped
	 * object
	 * @throws XmlRpcException if the <code>ParamIterator</code> failed
	 * to iterate
	 */
	public ParamIterator execute(String methodName, ParamIterator params) 
		throws XmlRpcException
	{
		// Need to copy params (at least first level) to List
		List l = null;
		if (params != null) {
			l = new ArrayList();
			while (params.hasNext())
				l.add(params.next());
		}

		// Array and Classtype:  Value building	
		Class[] argClasses = null;
		Object[] argValues =  null;
		if (l != null) {
			argClasses = new Class[l.size()];
			argValues = new Object[l.size()];
			for (int i = 0; i < l.size(); i++) {
				argValues[i] = l.get(i);
				if (argValues[i] instanceof Integer)
					argClasses[i] = Integer.TYPE;
				else if (argValues[i] instanceof Double)
					argClasses[i] = Double.TYPE;
				else if (argValues[i] instanceof Boolean)
					argClasses[i] = Boolean.TYPE;
				else 
					argClasses[i] = argValues[i].getClass();
			}
		}			
		
		// Method to invoke
		Method method = null;
		
		try {
			method = targetClass.getMethod(methodName, argClasses);
		}
		catch (NoSuchMethodException nsme) {			
			throw new InvokerException(nsme);
		}
		catch (SecurityException se) {
			throw new InvokerException(se);
		}
						
		// Call invoke
		Object returnValue = null;
		try {
			returnValue = method.invoke(target, argValues);
		}
		catch (IllegalAccessException iacce){
			throw new InvokerException(iacce);
		}
		catch (IllegalArgumentException iarge){
			throw new InvokerException(iarge);
		}
		catch (InvocationTargetException ite) {
			throw new InvokerException(ite.getTargetException());
		}
		return toParamIterator(returnValue);
	}

	private ParamIterator toParamIterator(Object o)
	{
		// Try to package return as Iterator, if possible
		if (o instanceof ParamIterator) {
			return (ParamIterator)o;
		}
		else if (o instanceof Iterator) {
			return new IteratorAdapter((Iterator)o);
		}
		else if (o instanceof Collection) {
			Collection c = (Collection)o;
			return new IteratorAdapter(c.iterator());
		}
		// We don't know what it is
		List l = new ArrayList();
		l.add(o);
		return toParamIterator(l.iterator());
	}

}
