/*
* RHQ Management Platform
* Copyright 2012, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.bindings.util;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.List;
import org.testng.Assert;
import org.testng.annotations.Test;
import org.rhq.core.domain.auth.Subject;
/**
* @author Lukas Krejci
*/
@Test
public class InterfaceSimplifierTest {
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.PARAMETER, ElementType.METHOD })
public static @interface MyAnnotation {
int value() default 1;
String parameter();
}
public static interface NoSimplifications {
void voidMethodWithNoExceptions();
void voidMethodWithExceptions() throws IOException, InterruptedException;
int intMethod();
Object objectMethod();
Object objectMethodWithParams(int p1, Object p2);
Object objectMethodWithParamsAndExceptions(int p1, Object p2) throws IOException, InterruptedException;
}
public static interface Simplifications {
void voidMethodWithNoExceptions(Subject s);
void voidMethodWithExceptions(Subject s) throws IOException, InterruptedException;
int intMethod(Subject s);
Object objectMethod(Subject s);
Object objectMethodWithParams(Subject s, int p1, Object p2);
Object objectMethodWithParamsAndExceptions(Subject s, int p1, Object p2) throws IOException,
InterruptedException;
}
public static interface Generics<C extends Type> {
void genericParameters(List<String> p, int p2);
<T extends Type> T typeParameters(T p, int p2);
<T extends Type> T typeParametersSimplified(Subject s, T p, int p2);
<T extends C> T classTypeParameters(T p, int p2);
}
@MyAnnotation(parameter = "CLASS")
public static interface Annotations {
@MyAnnotation(value = 2, parameter = "a")
int method(@MyAnnotation(parameter = "b") int p) throws IOException;
@MyAnnotation(value = 2, parameter = "c")
int methodSimplified(@MyAnnotation(parameter = "disappear") Subject s, @MyAnnotation(parameter = "d") int p)
throws IOException;
}
public void testNoSimplifications() throws Exception {
Class<?> iface = InterfaceSimplifier.simplify(NoSimplifications.class);
Method voidMethodWithNoExceptions = iface.getMethod("voidMethodWithNoExceptions");
Assert.assertEquals(voidMethodWithNoExceptions.getReturnType(), void.class);
Method voidMethodWithExceptions = iface.getMethod("voidMethodWithExceptions");
List<Class<?>> exceptions = Arrays.asList(voidMethodWithExceptions.getExceptionTypes());
Assert.assertTrue(exceptions.contains(IOException.class),
"The 'voidMethodWithExceptions doesn't seem to declare throws IOException");
Assert.assertTrue(exceptions.contains(InterruptedException.class),
"The 'voidMethodWithExceptions doesn't seem to declare throws InterruptedException");
Method intMethod = iface.getMethod("intMethod");
Assert.assertEquals(intMethod.getReturnType(), int.class);
Method objectMethod = iface.getMethod("objectMethod");
Assert.assertEquals(objectMethod.getReturnType(), Object.class);
Method objectMethodWithParams = iface.getMethod("objectMethodWithParams", int.class, Object.class);
Assert.assertEquals(objectMethodWithParams.getReturnType(), Object.class);
Method objectMethodWithParamsAndExceptions = iface.getMethod("objectMethodWithParamsAndExceptions", int.class,
Object.class);
Assert.assertEquals(objectMethodWithParamsAndExceptions.getReturnType(), Object.class);
exceptions = Arrays.asList(objectMethodWithParamsAndExceptions.getExceptionTypes());
Assert.assertTrue(exceptions.contains(IOException.class),
"The 'objectMethodWithParamsAndExceptions doesn't seem to declare throws IOException");
Assert.assertTrue(exceptions.contains(InterruptedException.class),
"The 'objectMethodWithParamsAndExceptions doesn't seem to declare throws InterruptedException");
}
public void testSimplifications() throws Exception {
Class<?> iface = InterfaceSimplifier.simplify(Simplifications.class);
//These tests are exactly the same as for the NoSimplifications class, because
//the simplifier should leave out all the subject parameters in the Simplifications
//class' methods.
Method voidMethodWithNoExceptions = iface.getMethod("voidMethodWithNoExceptions");
Assert.assertEquals(voidMethodWithNoExceptions.getReturnType(), void.class);
Method voidMethodWithExceptions = iface.getMethod("voidMethodWithExceptions");
List<Class<?>> exceptions = Arrays.asList(voidMethodWithExceptions.getExceptionTypes());
Assert.assertTrue(exceptions.contains(IOException.class),
"The 'voidMethodWithExceptions doesn't seem to declare throws IOException");
Assert.assertTrue(exceptions.contains(InterruptedException.class),
"The 'voidMethodWithExceptions doesn't seem to declare throws InterruptedException");
Method intMethod = iface.getMethod("intMethod");
Assert.assertEquals(intMethod.getReturnType(), int.class);
Method objectMethod = iface.getMethod("objectMethod");
Assert.assertEquals(objectMethod.getReturnType(), Object.class);
Method objectMethodWithParams = iface.getMethod("objectMethodWithParams", int.class, Object.class);
Assert.assertEquals(objectMethodWithParams.getReturnType(), Object.class);
Method objectMethodWithParamsAndExceptions = iface.getMethod("objectMethodWithParamsAndExceptions", int.class,
Object.class);
Assert.assertEquals(objectMethodWithParamsAndExceptions.getReturnType(), Object.class);
exceptions = Arrays.asList(objectMethodWithParamsAndExceptions.getExceptionTypes());
Assert.assertTrue(exceptions.contains(IOException.class),
"The 'objectMethodWithParamsAndExceptions doesn't seem to declare throws IOException");
Assert.assertTrue(exceptions.contains(InterruptedException.class),
"The 'objectMethodWithParamsAndExceptions doesn't seem to declare throws InterruptedException");
}
public <T> void testGenerics() throws Exception {
@SuppressWarnings("unchecked")
Class<T> iface = (Class<T>) InterfaceSimplifier.simplify(Generics.class);
TypeVariable<Class<T>>[] classTypeParameters = iface.getTypeParameters();
Assert.assertEquals(classTypeParameters.length, 1, "There should be 1 type parameter on the class Generics.");
TypeVariable<?> typeVariable = classTypeParameters[0];
Assert.assertEquals(typeVariable.getName(), "C", "Unexpected type parameter name on 'Generics' class.");
Type[] bounds = typeVariable.getBounds();
Assert.assertEquals(bounds.length, 1, "The type parameter on the class 'Generics' should have 1 upper bound.");
Assert.assertEquals(bounds[0], Type.class,
"The type parameter on the class 'Generics' should have the upper bound of the Type class.");
Method genericParameters = iface.getMethod("genericParameters", List.class, int.class);
Assert.assertEquals(genericParameters.getReturnType(), void.class);
Type firstParamType = genericParameters.getGenericParameterTypes()[0];
Assert.assertTrue(firstParamType instanceof ParameterizedType,
"The first parameter of the 'genericParameters' should be parameterized.");
Assert.assertEquals(((ParameterizedType) firstParamType).getRawType(), List.class,
"The first parameter of the 'genericParameters' method should be a List.");
Assert.assertEquals(((ParameterizedType) firstParamType).getActualTypeArguments()[0], String.class,
"The first parameter of the 'genericParamters' method should be a List<String>");
Method typeParameters = iface.getMethod("typeParameters", Type.class, int.class);
Assert.assertEquals(typeParameters.getReturnType(), Type.class);
TypeVariable<Method>[] typeVariables = typeParameters.getTypeParameters();
Assert.assertEquals(typeVariables.length, 1,
"There should be 1 type parameter on the the 'typeParameters' method.");
typeVariable = typeVariables[0];
Assert.assertEquals(typeVariable.getName(), "T", "Unexpected type parameter name on 'typeParameters' method.");
bounds = typeVariable.getBounds();
Assert.assertEquals(bounds.length, 1, "The type parameter on the method 'typeParameters' should have 1 upper bound.");
Assert.assertEquals(bounds[0], Type.class,
"The type parameter on the method 'typeParameters' should have the upper bound of the Type class.");
Type returnType = typeParameters.getGenericReturnType();
Assert.assertTrue(returnType instanceof TypeVariable,
"The generic return type of the 'typeParameters' class should be a type variable.");
typeVariable = (TypeVariable<?>) returnType;
Assert.assertEquals(typeVariable.getName(), "T",
"Unexpected type parameter at the return type of the 'typeParameters' method.");
Method typeParametersSimplified = iface.getMethod("typeParametersSimplified", Type.class, int.class);
Assert.assertEquals(typeParameters.getReturnType(), Type.class);
typeVariables = typeParametersSimplified.getTypeParameters();
Assert.assertEquals(typeVariables.length, 1,
"There should be 1 type parameter on the the 'typeParametersSimplified' method.");
typeVariable = typeVariables[0];
Assert.assertEquals(typeVariable.getName(), "T",
"Unexpected type parameter name on 'typeParametersSimplified' method.");
bounds = typeVariable.getBounds();
Assert.assertEquals(bounds.length, 1,
"The type parameter on the method 'typeParametersSimplified' should have 1 upper bound.");
Assert
.assertEquals(bounds[0], Type.class,
"The type parameter on the method 'typeParametersSimplified' should have the upper bound of the Type class.");
returnType = typeParametersSimplified.getGenericReturnType();
Assert.assertTrue(returnType instanceof TypeVariable,
"The generic return type of the 'typeParametersSimplified' class should be a type variable.");
typeVariable = (TypeVariable<?>) returnType;
Assert.assertEquals(typeVariable.getName(), "T",
"Unexpected type parameter at the return type of the 'typeParametersSimplified' method.");
}
public void testAnnotations() throws Exception {
Class<?> iface = InterfaceSimplifier.simplify(Annotations.class);
Annotation[] annotations = iface.getAnnotations();
//we add the @SimplifiedClass annotation
Assert.assertEquals(annotations.length, Annotations.class.getAnnotations().length + 1,
"Unexpected number of annotations on the 'Annotations' class.");
Annotation annotation = annotations[0];
Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
"Unexpected annotation type on the class 'Annotations");
Assert.assertEquals(((MyAnnotation) annotation).value(), 1,
"Unexpected value of the 'value' attribute on the annotation on the 'Annotations' class.");
Assert.assertEquals(((MyAnnotation) annotation).parameter(), "CLASS",
"Unexpected value of the 'parameter' attribute on the annotation on the 'Annotations' class.");
Method method = iface.getMethod("method", int.class);
annotations = method.getAnnotations();
Assert.assertEquals(annotations.length, 1, "Unexpected number of annotations on the 'method' method.");
annotation = annotations[0];
Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
"Unexpected annotation type on the method 'method");
Assert.assertEquals(((MyAnnotation) annotation).value(), 2,
"Unexpected value of the 'value' attribute on the annotation on the 'method' method.");
Assert.assertEquals(((MyAnnotation) annotation).parameter(), "a",
"Unexpected value of the 'parameter' attribute on the annotation on the 'method' method.");
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Assert
.assertEquals(parameterAnnotations.length, 1,
"Method 'Annotations.method(int)' has 1 parameter with annotations but we got a different number of parameters.");
Assert.assertEquals(parameterAnnotations[0].length, 1,
"The parameter of 'Annotations.method(int)' method has an annotation but we couldn't detect any.");
annotation = parameterAnnotations[0][0];
Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
"Unexpected annotation type on the parameter 'p' of 'Annotations.method(int)'");
Assert.assertEquals(((MyAnnotation) annotation).value(), 1,
"Unexpected value of the 'value' of the annotation on the parameter p of 'Annotations.method(int)'.");
Assert.assertEquals(((MyAnnotation) annotation).parameter(), "b",
"Unexpected value of the 'parameter' of the annotation on the parameter p of 'Annotations.method(int)'.");
method = iface.getMethod("methodSimplified", int.class);
annotations = method.getAnnotations();
//we add the @SimplifiedMethod on the method
Assert
.assertEquals(annotations.length, 2, "Unexpected number of annotations on the 'methodSimplified' method.");
annotation = annotations[0];
Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
"Unexpected annotation type on the method 'methodSimplified");
Assert.assertEquals(((MyAnnotation) annotation).value(), 2,
"Unexpected value of the 'value' attribute on the annotation on the 'methodSimplified' method.");
Assert.assertEquals(((MyAnnotation) annotation).parameter(), "c",
"Unexpected value of the 'parameter' attribute on the annotation on the 'methodSimplified' method.");
parameterAnnotations = method.getParameterAnnotations();
Assert
.assertEquals(parameterAnnotations.length, 1,
"Method 'Annotations.methodSimplified(int)' has 1 parameter with annotations but we got a different number of parameters.");
Assert.assertEquals(parameterAnnotations[0].length, 1,
"The parameter of 'Annotations.methodSimplified(int)' method has an annotation but we couldn't detect any.");
annotation = parameterAnnotations[0][0];
Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
"Unexpected annotation type on the parameter 'p' of 'Annotations.methodSimplified(int)'");
Assert.assertEquals(((MyAnnotation) annotation).value(), 1,
"Unexpected value of the 'value' of the annotation on the parameter p of 'Annotations.methodSimplified(int)'.");
Assert
.assertEquals(((MyAnnotation) annotation).parameter(), "d",
"Unexpected value of the 'parameter' of the annotation on the parameter p of 'Annotations.methodSimplified(int)'.");
}
public void testOriginalMethodRetrieval() throws Exception {
Class<?> iface = InterfaceSimplifier.simplify(Generics.class);
Method simplifiedMethod = iface.getMethod("typeParametersSimplified", Type.class, int.class);
Method origMethod = InterfaceSimplifier.getOriginalMethod(simplifiedMethod);
Assert.assertTrue(InterfaceSimplifier.isSimplified(iface),
"Unable to determine that the simplified interface was simplified.");
Assert.assertTrue(InterfaceSimplifier.isSimplified(simplifiedMethod));
Assert.assertFalse(InterfaceSimplifier.isSimplified(String.class), "String class is NOT simplified.");
Assert.assertFalse(InterfaceSimplifier.isSimplified(Object.class.getMethod("toString")),
"Object.toString() is NOT simplified.");
Assert.assertEquals(origMethod.getDeclaringClass(), Generics.class,
"Unexpected declaring class of the original method.");
Assert.assertEquals(origMethod.getParameterTypes().length, simplifiedMethod.getParameterTypes().length + 1,
"Unexpected number of params on the original method.");
Assert.assertEquals(origMethod.getParameterTypes()[0], Subject.class,
"Unexpected first param of the original method.");
Method nonSimplifiedMethod = iface.getMethod("typeParameters", Type.class, int.class);
origMethod = InterfaceSimplifier.getOriginalMethod(nonSimplifiedMethod);
Assert.assertFalse(InterfaceSimplifier.isSimplified(nonSimplifiedMethod));
Assert.assertEquals(origMethod.getDeclaringClass(), Generics.class,
"Unexpected declaring class of the original method.");
Assert.assertEquals(origMethod.getParameterTypes(), nonSimplifiedMethod.getParameterTypes(),
"Unexpected params on the original method.");
}
}