/* FacadeInvoker.java


	Purpose: 
	Description: 
	History:
	2001/11/27 20:06:18, Create, Tom M. Yeh.

Copyright (C) 2001 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
	This program is distributed under LGPL Version 2.1 in the hope that
	it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.lang.reflect;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;

import org.zkoss.lang.Classes;

/**
 * The facade invocation handler. Like the facade pattern, this invocation
 * handler is used to facade an object with a set of interfaces.
 *
 * <p>This Invocation handler assumes the object being facaded
 * (a.k.a, the target) has all methods of these interfaces, but it might not
 * implement all of these interfaces directly.
 *
 * <p>It happens when you need to provide interfaces to classes that
 * come from third parties.
 *
 * <p>Example:<br>
 *<pre><code>class A {
 *  public void f() {...}
 *}
 *interface I {
 *  public void f();
 *}</code>
 *Then, you could do:
 *<code>I i = (I)FacadeInvoker.newInstance(new A(), new Class[] {I});</code></pre>
 *
 * @author tomyeh
 */
public class FacadeInvoker implements InvocationHandler {
	/** The target that really implements the interfaces. */
	private Object _target;

	/**
	 * Creates an object that implements the giving interfaces by
	 * wrapping a giving object, target.
	 *
	 * <p>The target must have all methods in all given interfaces,
	 * but might not implement all these interfaces.
	 */
	public static final Object newInstance
	(Object target, Class[] interfaces, ClassLoader clsLoader) {
		return Proxy.newProxyInstance(
			clsLoader, interfaces, new FacadeInvoker(target));
	}
	/**
	 * Creates an object that implements the giving interfaces by
	 * wrapping a giving object, target.
	 * <p>A shortcut: newInstance(
	 * target, interfaces, Thread.currentThread().getContextClassLoader()).
	 */
	public static final Object newInstance(Object target, Class[] interfaces) {
		ClassLoader cl = Classes.getContextClassLoader(FacadeInvoker.class);
		return newInstance(target, interfaces,
			cl != null ? cl: FacadeInvoker.class.getClassLoader());
	}

	protected FacadeInvoker(Object target) {
		_target = target;
	}

	//-- InvocationInvoker --//
	public Object invoke(Object proxy, Method method, Object[] args)
	throws Throwable {
		Class cls = _target.getClass();
		if (!method.getDeclaringClass().isAssignableFrom(cls))
			method = Classes.getMethodInPublic(
					cls, method.getName(), method.getParameterTypes());

		try {
			return method.invoke(_target, args);
		}catch(InvocationTargetException ex) {
			throw ex.getCause();
		}
	}
}
