/*
* Copyright (C) 2007 The Guava 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 com.google.common.collect;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import junit.framework.TestCase;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Base test case for testing the variety of forwarding classes.
*
* @author Robert Konigsberg
* @author Louis Wasserman
*/
public abstract class ForwardingTestCase extends TestCase {
private List<String> calls = new ArrayList<String>();
private void called(String id) {
calls.add(id);
}
protected String getCalls() {
return calls.toString();
}
protected boolean isCalled() {
return !calls.isEmpty();
}
@SuppressWarnings("unchecked")
protected <T> T createProxyInstance(Class<T> c) {
/*
* This invocation handler only registers that a method was called,
* and then returns a bogus, but acceptable, value.
*/
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
called(asString(method));
return getDefaultValue(method.getReturnType());
}
};
return (T) Proxy.newProxyInstance(c.getClassLoader(),
new Class[] { c }, handler);
}
private static final Joiner COMMA_JOINER = Joiner.on(",");
/*
* Returns string representation of a method.
*
* If the method takes no parameters, it returns the name (e.g.
* "isEmpty". If the method takes parameters, it returns the simple names
* of the parameters (e.g. "put(Object,Object)".)
*/
private String asString(Method method) {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 0) {
return methodName;
}
Iterable<String> parameterNames = Iterables.transform(
Arrays.asList(parameterTypes),
new Function<Class<?>, String>() {
@Override
public String apply(Class<?> from) {
return from.getSimpleName();
}
});
return methodName + "(" + COMMA_JOINER.join(parameterNames) + ")";
}
private static Object getDefaultValue(Class<?> returnType) {
if (returnType == boolean.class || returnType == Boolean.class) {
return Boolean.FALSE;
} else if (returnType == int.class || returnType == Integer.class) {
return 0;
} else if ((returnType == Set.class) || (returnType == Collection.class)) {
return Collections.emptySet();
} else if (returnType == Iterator.class){
return Iterators.emptyModifiableIterator();
} else if (returnType.isArray()) {
return Array.newInstance(returnType.getComponentType(), 0);
} else {
return null;
}
}
protected static <T> void callAllPublicMethods(Class<T> theClass, T object)
throws InvocationTargetException {
for (Method method : theClass.getMethods()) {
Class<?>[] parameterTypes = method.getParameterTypes();
Object[] parameters = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
parameters[i] = getDefaultValue(parameterTypes[i]);
}
try {
try {
method.invoke(object, parameters);
} catch (InvocationTargetException ex) {
try {
throw ex.getCause();
} catch (UnsupportedOperationException unsupported) {
// this is a legit exception
}
}
} catch (Throwable cause) {
throw new InvocationTargetException(cause,
method.toString() + " with args: " + Arrays.toString(parameters));
}
}
}
}