/*
* Copyright (C) 2006-2015 INRIA and contributors
* Spoon - http://spoon.gforge.inria.fr/
*
* This software is governed by the CeCILL-C License under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL-C license as
* circulated by CEA, CNRS and INRIA at http://www.cecill.info.
*
* 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 CeCILL-C License for more details.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
package spoon.test.type;
import org.junit.Test;
import spoon.Launcher;
import spoon.reflect.code.BinaryOperatorKind;
import spoon.reflect.code.CtBinaryOperator;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtConstructorCall;
import spoon.reflect.code.CtFieldRead;
import spoon.reflect.code.CtLambda;
import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.code.CtNewClass;
import spoon.reflect.code.CtTypeAccess;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtTypeParameter;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtIntersectionTypeReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.support.SpoonClassNotFoundException;
import spoon.test.type.testclasses.Mole;
import spoon.test.type.testclasses.Pozole;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static spoon.testing.utils.ModelUtils.buildClass;
import static spoon.testing.utils.ModelUtils.canBeBuilt;
import static spoon.testing.utils.ModelUtils.createFactory;
public class TypeTest {
@Test
public void testTypeAccessForDotClass() throws Exception {
// contract: When we use .class on a type, this must be a CtTypeAccess.
final String target = "./target/type";
final Launcher launcher = new Launcher();
launcher.addInputResource("./src/test/java/spoon/test/type/testclasses");
launcher.setSourceOutputDirectory(target);
launcher.getEnvironment().setNoClasspath(true);
launcher.run();
final CtClass<Pozole> aPozole = launcher.getFactory().Class().get(Pozole.class);
final CtMethod<?> make = aPozole.getMethodsByName("make").get(0);
final List<CtFieldRead<?>> fieldClasses = make.getElements(new TypeFilter<CtFieldRead<?>>(CtFieldRead.class) {
@Override
public boolean matches(CtFieldRead<?> element) {
return "class".equals(element.getVariable().getSimpleName()) && super.matches(element);
}
});
assertEquals(4, fieldClasses.size());
for (CtFieldRead<?> fieldClass : fieldClasses) {
assertTrue(fieldClass.getTarget() instanceof CtTypeAccess);
}
canBeBuilt(target, 8, true);
}
@Test
public void testTypeAccessOnPrimitive() throws Exception {
Factory factory = createFactory();
CtClass<?> clazz = factory.Code().createCodeSnippetStatement( //
"class X {" //
+ "public void foo() {" //
+ " Class klass=null;" //
+ " boolean x= (klass == short.class);" //
+ "}};").compile();
CtMethod<?> foo = (CtMethod<?>) clazz.getMethods().toArray()[0];
CtBlock<?> body = foo.getBody();
CtLocalVariable<?> ass = body.getStatement(1);
CtBinaryOperator<?> op = (CtBinaryOperator<?>) ass.getDefaultExpression();
assertEquals("Class", op.getLeftHandOperand().getType().getSimpleName());
assertFalse(op.getLeftHandOperand().getType().isPrimitive());
assertEquals("Class", op.getRightHandOperand().getType().getSimpleName());
assertTrue(op.getRightHandOperand() instanceof CtFieldRead);
assertFalse(op.getRightHandOperand().getType().isPrimitive());
}
@Test
public void testTypeAccessForTypeAccessInInstanceOf() throws Exception {
// contract: the right hand operator must be a CtTypeAccess.
final String target = "./target/type";
final Launcher launcher = new Launcher();
launcher.addInputResource("./src/test/java/spoon/test/type/testclasses");
launcher.setSourceOutputDirectory(target);
launcher.getEnvironment().setNoClasspath(true);
launcher.run();
final CtClass<Pozole> aPozole = launcher.getFactory().Class().get(Pozole.class);
final CtMethod<?> eat = aPozole.getMethodsByName("eat").get(0);
final List<CtTypeAccess<?>> typeAccesses = eat.getElements(new TypeFilter<CtTypeAccess<?>>(CtTypeAccess.class));
assertEquals(2, typeAccesses.size());
assertTrue(typeAccesses.get(0).getParent() instanceof CtBinaryOperator);
assertEquals(BinaryOperatorKind.INSTANCEOF, ((CtBinaryOperator) typeAccesses.get(0).getParent()).getKind());
assertEquals("a instanceof java.lang.String", typeAccesses.get(0).getParent().toString());
assertTrue(typeAccesses.get(1).getParent() instanceof CtBinaryOperator);
assertEquals(BinaryOperatorKind.INSTANCEOF, ((CtBinaryOperator) typeAccesses.get(1).getParent()).getKind());
assertEquals("a instanceof java.util.Collection<?>", typeAccesses.get(1).getParent().toString());
}
@Test
public void testTypeAccessOfArrayObjectInFullyQualifiedName() throws Exception {
// contract: A type access in fully qualified name must to rewrite well.
final String target = "./target/type";
final Launcher launcher = new Launcher();
launcher.addInputResource("./src/test/java/spoon/test/type/testclasses");
launcher.setSourceOutputDirectory(target);
launcher.getEnvironment().setNoClasspath(true);
launcher.run();
final CtClass<Pozole> aPozole = launcher.getFactory().Class().get(Pozole.class);
final CtMethod<?> season = aPozole.getMethodsByName("season").get(0);
final List<CtTypeAccess<?>> typeAccesses = season.getElements(new TypeFilter<CtTypeAccess<?>>(CtTypeAccess.class));
assertEquals(2, typeAccesses.size());
assertTrue(typeAccesses.get(0).getParent() instanceof CtBinaryOperator);
assertEquals(BinaryOperatorKind.INSTANCEOF, ((CtBinaryOperator) typeAccesses.get(0).getParent()).getKind());
assertEquals("a instanceof java.lang.@spoon.test.annotation.testclasses.TypeAnnotation(integer = 1)" + System.lineSeparator() + "Object[]", typeAccesses.get(0).getParent().toString());
assertTrue(typeAccesses.get(1).getParent() instanceof CtBinaryOperator);
assertEquals(BinaryOperatorKind.INSTANCEOF, ((CtBinaryOperator) typeAccesses.get(1).getParent()).getKind());
assertEquals("a instanceof java.lang.Object[]", typeAccesses.get(1).getParent().toString());
canBeBuilt(target, 8, true);
}
@Test
public void test() throws Exception {
final Launcher launcher = new Launcher();
launcher.addInputResource("./src/test/resources/noclasspath/TorIntegration.java");
launcher.getEnvironment().setNoClasspath(true);
launcher.buildModel();
CtType<?> ctType = launcher.getFactory().Class().getAll().get(0);
List<CtNewClass> elements = ctType.getElements(new TypeFilter<>(CtNewClass.class));
assertEquals(4, elements.size());
for (int i = 0; i < elements.size(); i++) {
CtNewClass ctNewClass = elements.get(i);
assertEquals("android.content.DialogInterface$OnClickListener", ctNewClass.getAnonymousClass().getSuperclass().getQualifiedName());
}
}
@Test
public void testIntersectionTypeReferenceInGenericsAndCasts() throws Exception {
final String target = "./target/type";
final Launcher launcher = new Launcher();
launcher.addInputResource("./src/test/java/spoon/test/type/testclasses");
launcher.setSourceOutputDirectory(target);
launcher.getEnvironment().setNoClasspath(true);
launcher.run();
final CtClass<Pozole> aPozole = launcher.getFactory().Class().get(Pozole.class);
final CtMethod<?> prepare = aPozole.getMethodsByName("prepare").get(0);
// Intersection type in generic types.
final List<CtClass> localTypes = prepare.getElements(new TypeFilter<>(CtClass.class));
assertEquals(1, localTypes.size());
// New type parameter declaration.
final CtTypeParameter typeParameter = localTypes.get(0).getFormalCtTypeParameters().get(0);
assertNotNull(typeParameter);
assertEquals("T", typeParameter.getSimpleName());
assertIntersectionTypeForPozolePrepareMethod(aPozole, typeParameter.getSuperclass());
// Intersection type in casts.
final List<CtLambda<?>> lambdas = prepare.getElements(new TypeFilter<CtLambda<?>>(CtLambda.class));
assertEquals(1, lambdas.size());
assertEquals(1, lambdas.get(0).getTypeCasts().size());
assertTrue(lambdas.get(0).getTypeCasts().get(0) instanceof CtIntersectionTypeReference);
final CtIntersectionTypeReference<?> intersectionType = lambdas.get(0).getTypeCasts().get(0).asCtIntersectionTypeReference();
assertEquals("java.lang.Runnable & java.io.Serializable", intersectionType.toString());
assertEquals(aPozole.getFactory().Type().createReference(Runnable.class), intersectionType.getBounds().stream().collect(Collectors.toList()).get(0));
assertEquals(aPozole.getFactory().Type().createReference(Serializable.class), intersectionType.getBounds().stream().collect(Collectors.toList()).get(1));
canBeBuilt(target, 8, true);
}
private void assertIntersectionTypeForPozolePrepareMethod(CtClass<Pozole> aPozole, CtTypeReference<?> boundingType) {
assertNotNull(boundingType);
assertTrue(boundingType instanceof CtIntersectionTypeReference);
assertEquals("java.lang.Runnable & java.io.Serializable", boundingType.toString());
final CtIntersectionTypeReference<?> superType = boundingType.asCtIntersectionTypeReference();
assertEquals(aPozole.getFactory().Type().createReference(Runnable.class), superType.getBounds().stream().collect(Collectors.toList()).get(0));
assertEquals(aPozole.getFactory().Type().createReference(Serializable.class), superType.getBounds().stream().collect(Collectors.toList()).get(1));
}
@Test
public void testTypeReferenceInGenericsAndCasts() throws Exception {
final String target = "./target/type";
final Launcher launcher = new Launcher();
launcher.addInputResource("./src/test/java/spoon/test/type/testclasses");
launcher.setSourceOutputDirectory(target);
launcher.getEnvironment().setNoClasspath(true);
launcher.run();
final CtClass<Pozole> aPozole = launcher.getFactory().Class().get(Pozole.class);
final CtMethod<?> prepare = aPozole.getMethodsByName("finish").get(0);
// Intersection type in generic types.
final List<CtClass> localTypes = prepare.getElements(new TypeFilter<>(CtClass.class));
assertEquals(1, localTypes.size());
// New type parameter declaration.
final CtTypeParameter typeParameter = localTypes.get(0).getFormalCtTypeParameters().get(0);
assertNotNull(typeParameter);
assertEquals("T", typeParameter.getSimpleName());
assertIntersectionTypeForPozoleFinishMethod(aPozole, typeParameter.getSuperclass());
// Intersection type in casts.
final List<CtLambda<?>> lambdas = prepare.getElements(new TypeFilter<CtLambda<?>>(CtLambda.class));
assertEquals(1, lambdas.size());
assertEquals(1, lambdas.get(0).getTypeCasts().size());
assertEquals("java.lang.Runnable", lambdas.get(0).getTypeCasts().get(0).toString());
assertEquals(aPozole.getFactory().Type().createReference(Runnable.class), lambdas.get(0).getTypeCasts().get(0));
canBeBuilt(target, 8, true);
}
private void assertIntersectionTypeForPozoleFinishMethod(CtClass<Pozole> aPozole, CtTypeReference<?> boundingType) {
assertNotNull(boundingType);
assertEquals("java.lang.Runnable", boundingType.toString());
assertEquals(aPozole.getFactory().Type().createReference(Runnable.class), boundingType);
}
@Test
public void testIntersectionTypeOnTopLevelType() throws Exception {
final CtType<Mole> aMole = buildClass(Mole.class);
assertEquals(1, aMole.getFormalCtTypeParameters().size());
// New type parameter declaration.
final CtTypeParameter typeParameter = aMole.getFormalCtTypeParameters().get(0);
assertIntersectionTypeForMole(aMole, typeParameter.getSuperclass());
}
private void assertIntersectionTypeForMole(CtType<Mole> aMole, CtTypeReference<?> boundingType) {
assertNotNull(boundingType);
assertTrue(boundingType instanceof CtIntersectionTypeReference);
assertEquals(2, boundingType.asCtIntersectionTypeReference().getBounds().size());
assertEquals(Number.class, boundingType.asCtIntersectionTypeReference().getBounds().stream().collect(Collectors.toList()).get(0).getActualClass());
assertEquals(Comparable.class, boundingType.asCtIntersectionTypeReference().getBounds().stream().collect(Collectors.toList()).get(1).getActualClass());
assertEquals("public class Mole<NUMBER extends java.lang.Number & java.lang.Comparable<NUMBER>> {}", aMole.toString());
}
@Test
public void testUnboxingTypeReference() throws Exception {
// contract: When you call CtTypeReference#unbox on a class which doesn't exist
// in the spoon path, the method return the type reference itself.
final Factory factory = createFactory();
final CtTypeReference<Object> aReference = factory.Type().createReference("fr.inria.Spoon");
try {
final CtTypeReference<?> unbox = aReference.unbox();
assertEquals(aReference, unbox);
} catch (SpoonClassNotFoundException e) {
fail("Should never throw a SpoonClassNotFoundException.");
}
}
@Test
public void testDeclarationCreatedByFactory() throws Exception {
final Factory factory = createFactory();
assertNotNull(factory.Interface().create("fr.inria.ITest").getReference().getDeclaration());
assertNotNull(factory.Enum().create("fr.inria.ETest").getReference().getDeclaration());
}
@Test
public void testPolyTypBindingInTernaryExpression() throws Exception {
Launcher launcher = new Launcher();
launcher.addInputResource("./src/test/resources/noclasspath/ternary-bug");
launcher.getEnvironment().setNoClasspath(true);
launcher.buildModel();
CtType<Object> aType = launcher.getFactory().Type().get("de.uni_bremen.st.quide.persistence.transformators.IssueTransformator");
CtConstructorCall ctConstructorCall = aType.getElements(new TypeFilter<CtConstructorCall>(CtConstructorCall.class) {
@Override
public boolean matches(CtConstructorCall element) {
return "TOIssue".equals(element.getExecutable().getType().getSimpleName()) && super.matches(element);
}
}).get(0);
assertEquals(launcher.getFactory().Type().objectType(), ctConstructorCall.getExecutable().getParameters().get(9));
}
@Test
public void testShadowType() throws Exception {
/* Objects and factory have to be the sames */
Launcher launcher = new Launcher();
launcher.buildModel();
final CtClass<Object> objectCtClass = launcher.getFactory().Class().get(Object.class);
final CtClass<Object> objectCtClass1 = launcher.getFactory().Class().get(Object.class);
assertSame(objectCtClass, objectCtClass1);
assertSame(launcher.getFactory().Class(), objectCtClass.getFactory().Class());
assertSame(launcher.getFactory(), objectCtClass.getFactory());
assertSame(launcher.getFactory().Class(), objectCtClass1.getFactory().Class());
assertSame(launcher.getFactory(), objectCtClass1.getFactory());
assertSame(objectCtClass.getFactory().Class().get(objectCtClass.getActualClass()), objectCtClass);
assertSame(objectCtClass.getFactory().Class().get(Object.class), objectCtClass);
assertSame(objectCtClass1.getFactory().Class().get(objectCtClass1.getActualClass()), objectCtClass1);
assertSame(objectCtClass1.getFactory().Class().get(Object.class), objectCtClass1);
assertTrue(objectCtClass.isShadow());
assertEquals("java.lang.Object", objectCtClass.getQualifiedName());
final CtType<Object> objectCtType = launcher.getFactory().Type().get(Object.class);
final CtType<Object> objectCtType1 = launcher.getFactory().Type().get(Object.class);
assertSame(objectCtType, objectCtType1);
assertSame(launcher.getFactory().Type(), objectCtType.getFactory().Type());
assertSame(launcher.getFactory(), objectCtType.getFactory());
assertSame(launcher.getFactory().Type(), objectCtType1.getFactory().Type());
assertSame(launcher.getFactory(), objectCtType1.getFactory());
assertSame(objectCtType.getFactory().Type().get(objectCtType.getActualClass()), objectCtType);
assertSame(objectCtType.getFactory().Type().get(Object.class), objectCtType);
assertSame(objectCtType1.getFactory().Type().get(objectCtType1.getActualClass()), objectCtType1);
assertSame(objectCtType1.getFactory().Type().get(Object.class), objectCtType1);
assertTrue(objectCtClass.isShadow());
assertEquals("java.lang.Object", objectCtClass.getQualifiedName());
final List<String> methodNameList = Arrays.asList(Object.class.getDeclaredMethods()).stream().map(Method::getName).collect(Collectors.toList());
for (CtMethod<?> ctMethod : objectCtClass.getMethods()) {
assertTrue(methodNameList.contains(ctMethod.getSimpleName()));
assertTrue(ctMethod.getBody().getStatements().isEmpty());
}
}
}