package spoon.test.template;
import org.junit.Test;
import spoon.Launcher;
import spoon.compiler.SpoonResourceHelper;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtExpression;
import spoon.reflect.code.CtIf;
import spoon.reflect.code.CtInvocation;
import spoon.reflect.code.CtStatement;
import spoon.reflect.code.CtTry;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.factory.Factory;
import spoon.reflect.visitor.ModelConsistencyChecker;
import spoon.reflect.visitor.filter.NameFilter;
import spoon.support.compiler.FileSystemFile;
import spoon.support.template.Parameters;
import spoon.template.TemplateMatcher;
import spoon.test.template.testclasses.SecurityCheckerTemplate;
import java.io.File;
import java.io.Serializable;
import java.rmi.Remote;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class TemplateTest {
@Test
public void testTemplateInheritance() throws Exception {
Launcher spoon = new Launcher();
Factory factory = spoon.getFactory();
spoon.getEnvironment().setCommentEnabled(true);
spoon.createCompiler(
factory,
SpoonResourceHelper.resources(
"./src/test/java/spoon/test/template/SubClass.java",
"./src/test/java/spoon/test/template/SuperClass.java"),
SpoonResourceHelper
.resources(
"./src/test/java/spoon/test/template/SubTemplate.java",
"./src/test/java/spoon/test/template/SuperTemplate.java"))
.build();
CtClass<?> superc = factory.Class().get(SuperClass.class);
new SuperTemplate().apply(superc);
CtMethod<?> addedMethod = superc.getElements(
new NameFilter<CtMethod<?>>("toBeOverriden")).get(0);
assertEquals("toBeOverriden", addedMethod.getSimpleName());
CtClass<?> subc = factory.Class().get(SubClass.class);
SubTemplate template = new SubTemplate();
template.params = new ArrayList<>();
CtParameter<Integer> parameter = factory.Core().createParameter();
parameter.setSimpleName("x");
parameter.setType(factory.Type().createReference(int.class));
template.params.add(parameter);
// templating the invocation
template.invocation = factory.Code().createInvocation(null, addedMethod.getReference());
// templating the foreach
template.intValues = new CtExpression[2];
template.intValues[0] = factory.Code().createLiteral(0);
template.intValues[1] = factory.Code().createLiteral(1);
// we apply the extension template to subc
template.apply(subc);
CtMethod<?> addedMethod2 = subc.getElements(
new NameFilter<CtMethod<?>>("toBeOverriden")).get(0);
assertEquals("toBeOverriden", addedMethod2.getSimpleName());
assertEquals("super.toBeOverriden()", addedMethod2.getBody()
.getStatements().get(0).toString());
// contract; method parameter templates are handled
CtMethod<?> methodWithTemplatedParameters = subc.getElements(
new NameFilter<CtMethod<?>>("methodWithTemplatedParameters")).get(0);
assertEquals("methodWithTemplatedParameters", methodWithTemplatedParameters.getSimpleName());
assertEquals("x", methodWithTemplatedParameters.getParameters().get(0).getSimpleName());
assertEquals("int x", methodWithTemplatedParameters.getParameters().get(0).toString());
// contract: nested types of the templates are copied
assertEquals(1, subc.getNestedTypes().size());
// contract: variable are renamed
assertEquals("java.util.List newVarName = null", methodWithTemplatedParameters.getBody().getStatement(0).toString());
// contract: types are replaced by other types
assertEquals("java.util.LinkedList l = null", methodWithTemplatedParameters.getBody().getStatement(1).toString());
// contract: casts are replaced by substitution types
assertEquals("java.util.List o = ((java.util.LinkedList) (new java.util.LinkedList()))", methodWithTemplatedParameters.getBody().getStatement(2).toString());
// contract: invocations are replaced by actual invocations
assertEquals("toBeOverriden()", methodWithTemplatedParameters.getBody().getStatement(3).toString());
// contract: foreach are inlined
CtBlock templatedForEach = methodWithTemplatedParameters.getBody().getStatement(4);
assertEquals("java.lang.System.out.println(0)", templatedForEach.getStatement(0).toString());
assertEquals("java.lang.System.out.println(1)", templatedForEach.getStatement(1).toString());
}
@Test
public void testTemplateC1() throws Exception {
Launcher spoon = new Launcher();
Factory factory = spoon.createFactory();
spoon.createCompiler(
factory,
SpoonResourceHelper
.resources("./src/test/java/spoon/test/template/C1.java"),
SpoonResourceHelper
.resources(
"./src/test/java/spoon/test/template/TemplateWithConstructor.java",
"./src/test/java/spoon/test/template/TemplateWithFieldsAndMethods.java"))
.build();
CtClass<?> c1 = factory.Class().get(C1.class);
// before template: 1 constructor
assertEquals(1, // this is the default implicit constructor
c1.getConstructors().size());
// the actual substitution
new TemplateWithConstructor(factory.Type()
.createReference(Date.class))
.apply(c1);
// after template: 3 constructors
// System.out.println("==>"+c1.getConstructors());
assertEquals(3, c1.getConstructors().size());
CtField<?> toBeInserted = c1.getElements(
new NameFilter<CtField<?>>("toBeInserted")).get(0);
assertEquals(Date.class, toBeInserted.getType()
.getActualTypeArguments().get(0).getActualClass());
assertEquals(
"java.util.List<java.util.Date> toBeInserted = new java.util.ArrayList<java.util.Date>();",
toBeInserted.toString());
new TemplateWithFieldsAndMethods(
"testparam", factory.Code().createLiteral("testparam2")).apply(c1);
assertEquals(3, c1.getConstructors().size());
assertNotNull(c1.getField("fieldToBeInserted"));
CtMethod<?> m = c1.getMethod("methodToBeInserted");
assertNotNull(m);
assertEquals("return \"testparam\"", m.getBody().getStatement(0)
.toString());
CtMethod<?> m2 = c1.getMethod("methodToBeInserted2");
assertNotNull(m2);
assertEquals("return \"testparam2\"", m2.getBody().getStatement(0)
.toString());
new ModelConsistencyChecker(factory.getEnvironment(), false, true).scan(c1);
assertEquals(0, factory.getEnvironment().getErrorCount());
assertEquals(0, factory.getEnvironment().getWarningCount());
}
@Test
public void testCheckBoundTemplate() throws Exception {
Launcher spoon = new Launcher();
Factory factory = spoon.createFactory();
spoon.createCompiler(
factory,
SpoonResourceHelper.resources(
"./src/test/java/spoon/test/template/FooBound.java"),
SpoonResourceHelper
.resources(
"./src/test/java/spoon/test/template/CheckBoundTemplate.java"))
.build();
CtClass<?> c = factory.Class().get(FooBound.class);
CtMethod<?> method = c.getMethodsByName("method").get(0);
assertEquals(1, Parameters.getAllTemplateParameterFields(CheckBoundTemplate.class).size());
assertEquals(1, Parameters.getAllTemplateParameterFields(CheckBoundTemplate.class, factory).size());
// creating a template instance
CheckBoundTemplate t = new CheckBoundTemplate();
assertTrue(t.isWellFormed());
assertFalse(t.isValid());
CtParameter<?> param = method.getParameters().get(0);
t.setVariable(param);
assertTrue(t.isValid());
// getting the final AST
CtStatement injectedCode = (t.apply(null));
assertTrue(injectedCode instanceof CtIf);
CtIf ifStmt = (CtIf) injectedCode;
// contains the replaced code
assertEquals("(l.size()) > 10", ifStmt.getCondition().toString());
// adds the bound check at the beginning of a method
method.getBody().insertBegin(injectedCode);
assertEquals(injectedCode, method.getBody().getStatement(0));
}
@Test
public void testTemplateMatcher() throws Exception {
// contract: the given templates should match the expected elements
Launcher spoon = new Launcher();
Factory factory = spoon.getFactory();
spoon.createCompiler(
factory,
SpoonResourceHelper.resources("./src/test/java/spoon/test/template/CheckBound.java"),
SpoonResourceHelper.resources("./src/test/java/spoon/test/template/CheckBoundMatcher.java"))
.build();
{// testing matcher1
CtClass<?> templateKlass = factory.Class().get(CheckBoundMatcher.class);
CtClass<?> klass = factory.Class().get(CheckBound.class);
CtIf templateRoot = (CtIf) ((CtMethod) templateKlass.getElements(new NameFilter("matcher1")).get(0)).getBody().getStatement(0);
TemplateMatcher matcher = new TemplateMatcher(templateRoot);
assertEquals(2, matcher.find(klass).size());
}
{// testing matcher2
CtClass<?> templateKlass = factory.Class().get(CheckBoundMatcher.class);
CtClass<?> klass = factory.Class().get(CheckBound.class);
CtIf templateRoot = (CtIf) ((CtMethod) templateKlass.getElements(new NameFilter("matcher2")).get(0)).getBody().getStatement(0);
TemplateMatcher matcher = new TemplateMatcher(templateRoot);
assertEquals(1, matcher.find(klass).size());
}
{// testing matcher3
CtClass<?> templateKlass = factory.Class().get(CheckBoundMatcher.class);
CtClass<?> klass = factory.Class().get(CheckBound.class);
CtIf templateRoot = (CtIf) ((CtMethod) templateKlass.getElements(new NameFilter("matcher3")).get(0)).getBody().getStatement(0);
TemplateMatcher matcher = new TemplateMatcher(templateRoot);
assertEquals(2, matcher.find(klass).size());
}
{// testing matcher4
CtClass<?> templateKlass = factory.Class().get(CheckBoundMatcher.class);
CtClass<?> klass = factory.Class().get(CheckBound.class);
CtIf templateRoot = (CtIf) ((CtMethod) templateKlass.getElements(new NameFilter("matcher4")).get(0)).getBody().getStatement(0);
TemplateMatcher matcher = new TemplateMatcher(templateRoot);
assertEquals(3, matcher.find(klass).size());
}
{// testing matcher5
CtClass<?> templateKlass = factory.Class().get(CheckBoundMatcher.class);
CtClass<?> klass = factory.Class().get(CheckBound.class);
CtIf templateRoot = (CtIf) ((CtMethod) templateKlass.getElements(new NameFilter("matcher5")).get(0)).getBody().getStatement(0);
TemplateMatcher matcher = new TemplateMatcher(templateRoot);
assertEquals(6, matcher.find(klass).size());
}
{// testing matcher6
CtClass<?> templateKlass = factory.Class().get(CheckBoundMatcher.class);
CtClass<?> klass = factory.Class().get(CheckBound.class);
CtIf templateRoot = (CtIf) ((CtMethod) templateKlass.getElements(new NameFilter("matcher6")).get(0)).getBody().getStatement(0);
TemplateMatcher matcher = new TemplateMatcher(templateRoot);
assertEquals(2, matcher.find(klass).size());
}
// testing with named elements, at the method level
{
CtClass<?> templateKlass = factory.Class().get(CheckBoundMatcher.class);
CtClass<?> klass = factory.Class().get(CheckBound.class);
CtMethod meth = (CtMethod) templateKlass.getElements(new NameFilter("matcher3")).get(0);
// exact match
meth.setSimpleName("foo");
TemplateMatcher matcher = new TemplateMatcher(meth);
List<CtMethod> ctElements = matcher.find(klass);
assertEquals(1, ctElements.size());
assertEquals("foo", ctElements.get(0).getSimpleName());
}
{
// contract: the name to be matched does not have to be an exact match
CtClass<?> templateKlass = factory.Class().get(CheckBoundMatcher.class);
CtClass<?> klass = factory.Class().get(CheckBound.class);
CtMethod meth = (CtMethod) templateKlass.getElements(new NameFilter("matcher4")).get(0);
// together with the appropriate @Parameter f, this means
// we match all methods with name f*
meth.setSimpleName("f");
TemplateMatcher matcher = new TemplateMatcher(meth);
List<CtMethod> ctElements = matcher.find(klass);
assertEquals(3, ctElements.size());
assertEquals("foo", ctElements.get(0).getSimpleName());
assertEquals("foo2", ctElements.get(1).getSimpleName());
assertEquals("fbar", ctElements.get(2).getSimpleName());
}
}
@Test
public void testExtensionBlock() throws Exception {
final Launcher launcher = new Launcher();
launcher.setArgs(new String[] {"--output-type", "nooutput" });
launcher.addInputResource("./src/test/java/spoon/test/template/Logger.java");
launcher.addTemplateResource(new FileSystemFile("./src/test/java/spoon/test/template/LoggerTemplate.java"));
launcher.addProcessor(new LoggerTemplateProcessor());
launcher.getEnvironment().setSourceClasspath(System.getProperty("java.class.path").split(File.pathSeparator));
try {
launcher.run();
} catch (ClassCastException ignored) {
fail();
}
final CtClass<Logger> aLogger = launcher.getFactory().Class().get(Logger.class);
final CtMethod aMethod = aLogger.getMethodsByName("enter").get(0);
assertTrue(aMethod.getBody().getStatement(0) instanceof CtTry);
final CtTry aTry = (CtTry) aMethod.getBody().getStatement(0);
assertTrue(aTry.getFinalizer().getStatement(0) instanceof CtInvocation);
assertEquals("spoon.test.template.Logger.exit(\"enter\")", aTry.getFinalizer().getStatement(0).toString());
assertTrue(aTry.getBody().getStatement(0) instanceof CtInvocation);
assertEquals("spoon.test.template.Logger.enter(\"Logger\", \"enter\")", aTry.getBody().getStatement(0).toString());
assertTrue(aTry.getBody().getStatements().size() > 1);
}
@Test
public void testTemplateInterfaces() throws Exception {
Launcher spoon = new Launcher();
Factory factory = spoon.getFactory();
spoon.createCompiler(
factory,
SpoonResourceHelper.resources(
"./src/test/java/spoon/test/template/SubClass.java"),
SpoonResourceHelper
.resources(
"./src/test/java/spoon/test/template/InterfaceTemplate.java")
)
.build();
CtClass<?> superc = factory.Class().get(SuperClass.class);
InterfaceTemplate interfaceTemplate = new InterfaceTemplate(superc.getFactory());
interfaceTemplate.apply(superc);
assertEquals(3, superc.getSuperInterfaces().size());
assertTrue(superc.getSuperInterfaces().contains(factory.Type().createReference(Comparable.class)));
assertTrue(superc.getSuperInterfaces().contains(factory.Type().createReference(Serializable.class)));
assertTrue(superc.getSuperInterfaces().contains(factory.Type().createReference(Remote.class)));
}
@Test
public void testTemplateMatcherWithWholePackage() throws Exception {
Launcher spoon = new Launcher();
spoon.addInputResource("./src/test/java/spoon/test/template/testclasses/ContextHelper.java");
spoon.addInputResource("./src/test/java/spoon/test/template/testclasses/BServiceImpl.java");
spoon.addTemplateResource(new FileSystemFile("./src/test/java/spoon/test/template/testclasses/SecurityCheckerTemplate.java"));
spoon.buildModel();
Factory factory = spoon.getFactory();
CtClass<?> templateKlass = factory.Class().get(SecurityCheckerTemplate.class);
CtMethod templateMethod = (CtMethod) templateKlass.getElements(new NameFilter("matcher1")).get(0);
CtIf templateRoot = (CtIf) templateMethod.getBody().getStatement(0);
TemplateMatcher matcher = new TemplateMatcher(templateRoot);
List<CtElement> matches = matcher.find(factory.getModel().getRootPackage());
assertEquals(1, matches.size());
CtElement match = matches.get(0);
assertTrue("Match is not a if", match instanceof CtIf);
CtElement matchParent = match.getParent();
assertTrue("Match parent is not a block", matchParent instanceof CtBlock);
CtElement matchParentParent = matchParent.getParent();
assertTrue("Match grand parent is not a method", matchParentParent instanceof CtMethod);
CtMethod methodHello = (CtMethod)matchParentParent;
assertEquals("Match grand parent is not a method called hello", "hello", methodHello.getSimpleName());
CtElement methodParent = methodHello.getParent();
assertTrue("Parent of the method is not a class",methodParent instanceof CtClass);
CtClass bservice = (CtClass) methodParent;
assertEquals("Parent of the method is not a class called BServiceImpl", "BServiceImpl", bservice.getSimpleName());
}
@Test
public void testTemplateMatcherMatchTwoSnippets() throws Exception {
Launcher spoon = new Launcher();
spoon.addInputResource("./src/test/java/spoon/test/template/testclasses/TwoSnippets.java");
spoon.addTemplateResource(new FileSystemFile("./src/test/java/spoon/test/template/testclasses/SecurityCheckerTemplate.java"));
spoon.buildModel();
Factory factory = spoon.getFactory();
CtClass<?> templateKlass = factory.Class().get(SecurityCheckerTemplate.class);
CtMethod templateMethod = (CtMethod) templateKlass.getElements(new NameFilter("matcher1")).get(0);
CtIf templateRoot = (CtIf) templateMethod.getBody().getStatement(0);
TemplateMatcher matcher = new TemplateMatcher(templateRoot);
//match using legacy TemplateMatcher#find method
List<CtElement> matches = matcher.find(factory.getModel().getRootPackage());
assertEquals(2, matches.size());
CtElement match1 = matches.get(0);
CtElement match2 = matches.get(1);
assertTrue(match1.equals(match2));
//match using TemplateMatcher#matches method and query filter
matches = factory.getModel().getRootPackage().filterChildren(matcher).list();
assertEquals(2, matches.size());
match1 = matches.get(0);
match2 = matches.get(1);
assertTrue(match1.equals(match2));
}
}