/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.api.internal.tasks.testing.testng;
import org.gradle.internal.reflect.JavaMethod;
import org.gradle.internal.reflect.JavaReflectionUtil;
import org.testng.ISuiteListener;
import org.testng.ITestListener;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
class TestNGListenerAdapterFactory {
private final ClassLoader classLoader;
TestNGListenerAdapterFactory(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public ITestListener createAdapter(ITestListener listener) {
Class<?> testNG6Class = tryLoadClass("org.testng.IConfigurationListener2");
if (testNG6Class != null) {
return createProxy(testNG6Class, listener);
}
Class<?> testNG5Class = tryLoadClass("org.testng.internal.IConfigurationListener");
if (testNG5Class != null) {
return createProxy(testNG5Class, listener);
}
throw new UnsupportedOperationException("Neither found interface 'org.testng.IConfigurationListener2' nor interface 'org.testng.internal.IConfigurationListener'. Which version of TestNG are you using?");
}
private Class<?> tryLoadClass(String name) {
try {
return classLoader.loadClass(name);
} catch (ClassNotFoundException e) {
return null;
}
}
private ITestListener createProxy(Class<?> configListenerClass, final ITestListener listener) {
Class<?>[] interfaces = new Class<?>[]{ITestListener.class, ISuiteListener.class, configListenerClass};
return (ITestListener) Proxy.newProxyInstance(classLoader, interfaces, new AdaptedListener(listener));
}
private static class AdaptedListener implements InvocationHandler {
private final ITestListener delegate;
private AdaptedListener(ITestListener delegate) {
this.delegate = delegate;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> realReturnType = method.getReturnType();
Class<?> boxedReturnType = realReturnType;
if (!realReturnType.equals(void.class) && realReturnType.isPrimitive()) {
boxedReturnType = JavaReflectionUtil.getWrapperTypeForPrimitiveType(realReturnType);
}
if (method.getName().equals("equals") && args != null && args.length == 1) {
return proxyEquals(proxy, args[0]);
}
if (method.getName().equals("hashCode") && args == null) {
return proxyHashCode(proxy);
}
return invoke(delegate.getClass(), delegate, boxedReturnType, method, args);
}
private <T, R> R invoke(Class<T> listenerType, Object listener, Class<R> returnType, Method method, Object[] args) {
T listenerCast = listenerType.cast(listener);
JavaMethod<T, R> javaMethod = JavaReflectionUtil.method(listenerType, returnType, method.getName(), method.getParameterTypes());
return javaMethod.invoke(listenerCast, args);
}
private boolean proxyEquals(Object proxy, Object other) {
if (other == null) {
return false;
}
if (proxy == other) {
return true;
}
if (!Proxy.isProxyClass(other.getClass())) {
return false;
}
InvocationHandler otherHandler = Proxy.getInvocationHandler(other);
if (!(otherHandler instanceof AdaptedListener)) {
return false;
}
AdaptedListener proxyAdapter = (AdaptedListener) Proxy.getInvocationHandler(proxy);
AdaptedListener otherAdapter = (AdaptedListener) otherHandler;
return proxyAdapter.getClass().equals(otherHandler.getClass())
&& proxyAdapter.delegate.getClass().equals(otherAdapter.delegate.getClass());
}
private int proxyHashCode(Object proxy) {
AdaptedListener invocationHandler = (AdaptedListener) Proxy.getInvocationHandler(proxy);
return Arrays.hashCode(new Object[] {invocationHandler.getClass(), invocationHandler.delegate.getClass()});
}
}
}