Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add tests and doc for template engine #1072

Merged
merged 1 commit into from
Jan 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 37 additions & 2 deletions doc/template_definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Using method `apply()` enables to get a new block.
Using method `apply()` enables to get a new expression. The core template method must be called `expression` and only contain a return with the expression to be templated.

#### Subclassing `ExtensionTemplate`
Using method `apply()` enables to get a new class where:
Using method `apply()` enables to get a new class where all possible templating in all methods. In addition, the following class level transformations are made:

1) methods and field of the templates are injected in the target class

Expand Down Expand Up @@ -126,7 +126,7 @@ Substitution.insertAll(aCtClass, t);

```

3) method parameters
3) method parameters are replaced

```java
class ATemplate3 extends ExtensionTemplate {
Expand Down Expand Up @@ -171,6 +171,41 @@ public class TryCatchOutOfBoundTemplate extends BlockTemplate {
}
```

Similarly, templated invocations require to declare a template parameter

```java
@Parameter
CtInvocation invocation;
```
and then all `invocation.S()` will be replaced by the actual invocation.

#### Inlining foreach expressions

Foreach expressions can be inlined. They have to be declared as follows:
```java
@Parameter
CtExpression[] intValues;
...
template.intValues = new CtExpression[2];
template.intValues[0] = factory.Code().createLiteral(0);
template.intValues[1] = factory.Code().createLiteral(1);
```

and then,

```java
for(Object x : intValues) {
System.out.println(x);
}
```
is transformed into:

```java
{
java.lang.System.out.println(0);
java.lang.System.out.println(1);
}
```
#### Literal template Parameters

For literals, Spoon provides developers with *literal template parameters*. When the parameter is known to
Expand Down
210 changes: 15 additions & 195 deletions src/main/java/spoon/support/template/SubstitutionVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import spoon.SpoonException;
Expand All @@ -32,47 +31,37 @@
import spoon.reflect.code.CtFieldWrite;
import spoon.reflect.code.CtForEach;
import spoon.reflect.code.CtInvocation;
import spoon.reflect.code.CtLiteral;
import spoon.reflect.code.CtReturn;
import spoon.reflect.code.CtStatement;
import spoon.reflect.code.CtSuperAccess;
import spoon.reflect.code.CtThisAccess;
import spoon.reflect.code.CtVariableAccess;
import spoon.reflect.code.CtVariableRead;
import spoon.reflect.code.CtVariableWrite;
import spoon.reflect.declaration.CtAnnotation;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtConstructor;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtExecutable;
import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtNamedElement;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtTypeMember;
import spoon.reflect.declaration.CtTypedElement;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtArrayTypeReference;
import spoon.reflect.reference.CtExecutableReference;
import spoon.reflect.reference.CtFieldReference;
import spoon.reflect.reference.CtReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.CtInheritanceScanner;
import spoon.reflect.visitor.CtScanner;
import spoon.reflect.visitor.Query;
import spoon.reflect.visitor.filter.VariableAccessFilter;
import spoon.template.Local;
import spoon.template.Parameter;
import spoon.template.Template;
import spoon.template.TemplateParameter;

class SkipException extends SpoonException {
class DoNotFurtherTemplateThisElement extends SpoonException {
private static final long serialVersionUID = 1L;

Object skipped;

SkipException(Object e) {
DoNotFurtherTemplateThisElement(Object e) {
super("skipping " + e.toString());
skipped = e;
}
Expand Down Expand Up @@ -171,57 +160,7 @@ private String substituteInDocComment(String docComment) {
return result;
}

/**
* Removes all the elements that are part of the template definitions.
*
* @see Template
* @see TemplateParameter
* @see Local
* @see Parameter
*/
@Override
public <T> void visitCtClass(CtClass<T> ctClass) {
ctClass.removeSuperInterface(factory.Type().createReference(Template.class));
for (CtMethod<?> m : new ArrayList<>(ctClass.getMethods())) { // copy is required for removing while iterating
if (m.getAnnotation(Local.class) != null) {
ctClass.removeMethod(m);
}
}
for (Iterator<CtConstructor<T>> it = ctClass.getConstructors().iterator(); it.hasNext();) {
CtConstructor<?> c = it.next();
if (c.getAnnotation(Local.class) != null) {
it.remove();
}
}
for (CtTypeMember m : new ArrayList<>(ctClass.getTypeMembers())) { // copy is required for removing while iterating
if (!(m instanceof CtField)) {
continue;
}
CtField field = (CtField) m;
if ((field.getAnnotation(Local.class) != null) || Parameters.isParameterSource(field.getReference())) {
ctClass.removeField(field);
continue;
}
// replace fields parameters
String name = field.getSimpleName();
for (String pname : parameterNames) {
if (name.equals(pname)) {
Object value = Parameters.getValue(template, pname, null);
int i = ctClass.getTypeMembers().indexOf(field);
if (value instanceof List) {
List<?> list = (List<?>) value;
for (Object f : list) {
CtField<?> f2 = ((CtField<?>) f).clone();
ctClass.addTypeMemberAt(i++, f2);
}
ctClass.removeTypeMember(field);
}
}
}
}
super.visitCtClass(ctClass);
}

/** statically inline foreach */
@Override
public void visitCtForEach(CtForEach foreach) {
if (foreach.getExpression() instanceof CtFieldAccess) {
Expand All @@ -235,10 +174,13 @@ public void visitCtForEach(CtForEach foreach) {
for (CtVariableAccess<?> va : Query.getElements(b, new VariableAccessFilter<>(foreach.getVariable().getReference()))) {
va.replace((CtExpression) element);
}
if (b instanceof CtBlock && ((CtBlock) b).getStatements().size() == 1) {
b = ((CtBlock) b).getStatement(0);
}
l.addStatement(b);
}
foreach.replace(l);
throw new SkipException(foreach);
throw new DoNotFurtherTemplateThisElement(foreach);
}
}
super.visitCtForEach(foreach);
Expand All @@ -263,7 +205,7 @@ private <T> void visitFieldAccess(CtFieldAccess<T> fieldAccess) {
Object[] value = (Object[]) Parameters.getValue(template, ref.getSimpleName(), null);
fieldAccess.replace((CtExpression) fieldAccess.getFactory().Code().createLiteral(value
.length));
throw new SkipException(fieldAccess);
throw new DoNotFurtherTemplateThisElement(fieldAccess);
}
}
}
Expand Down Expand Up @@ -306,7 +248,7 @@ private <T> void visitFieldAccess(CtFieldAccess<T> fieldAccess) {
toReplace.clone();
}
// do not visit if replaced
throw new SkipException(fieldAccess);
throw new DoNotFurtherTemplateThisElement(fieldAccess);
}
}

Expand All @@ -316,9 +258,6 @@ private <T> void visitFieldAccess(CtFieldAccess<T> fieldAccess) {
@SuppressWarnings("unchecked")
@Override
public <T> void visitCtInvocation(CtInvocation<T> invocation) {
if (invocation.getExecutable().getDeclaringType() == null) {
System.err.println(invocation.getExecutable());
}
if (invocation.getExecutable().isOverriding(S)) {
CtFieldAccess<?> fa = null;
if ((invocation.getTarget() instanceof CtFieldAccess)) {
Expand Down Expand Up @@ -351,7 +290,7 @@ public <T> void visitCtInvocation(CtInvocation<T> invocation) {
}
}
// do not visit the invocation if replaced
throw new SkipException(invocation);
throw new DoNotFurtherTemplateThisElement(invocation);
}
super.visitCtInvocation(invocation);
}
Expand All @@ -375,26 +314,6 @@ public <T> void scanCtExpression(CtExpression<T> expression) {
}
}
}
if (expression instanceof CtLiteral) {
CtLiteral<Object> lit = (CtLiteral<Object>) expression;
if (lit.getValue() instanceof CtTypeReference) {
CtTypeReference<T> t = (CtTypeReference<T>) lit.getValue();
if (parameterNames.contains(t.getSimpleName())) {
// replace type parameters
// TODO: this would probably not work with inner
// classes!!!
Object o = Parameters.getValue(template, t.getSimpleName(), null);
if (o instanceof Class) {
t = factory.Type().createReference(((Class<T>) o));
} else if (o instanceof CtTypeReference) {
t = (CtTypeReference<T>) o;
lit.setValue(t);
} else {
throw new RuntimeException("unsupported reference substitution");
}
}
}
}
super.scanCtExpression(expression);
}

Expand All @@ -407,7 +326,6 @@ public <T> void scanCtTypedElement(CtTypedElement<T> e) {
CtTypeReference<T> t;
Object o = Parameters.getValue(template, e.getType().getSimpleName(), null);
if (o instanceof Class) {
// TODO: CHECK THAT THIS IS STILL WORKING
o = factory.Type().createReference(((Class<T>) o));
}
if (o instanceof CtTypeReference) {
Expand All @@ -430,11 +348,6 @@ public <T> void scanCtTypedElement(CtTypedElement<T> e) {
@Override
public <T> void visitCtExecutableReference(CtExecutableReference<T> reference) {
scanCtReference(reference);
if (reference.getDeclaringType() == null) {
System.err.println("Blabla");
System.err.println(reference);
System.err.println(reference.getParent());
}
visitCtTypeReference(reference.getDeclaringType());
scanCtActualTypeContainer(reference);
}
Expand Down Expand Up @@ -489,70 +402,6 @@ public <T> void visitCtTypeReference(CtTypeReference<T> reference) {
}
super.visitCtTypeReference(reference);
}

@Override
public <T> void visitCtVariableRead(CtVariableRead<T> variableRead) {
if (visitVariableAccess(variableRead)) {
super.visitCtVariableRead(variableRead);
}
}

@Override
public <T> void visitCtVariableWrite(CtVariableWrite<T> variableWrite) {
if (visitVariableAccess(variableWrite)) {
super.visitCtVariableWrite(variableWrite);
}
}

private <T> boolean visitVariableAccess(CtVariableAccess<T> variableAccess) {
if (variableAccess instanceof CtSuperAccess) {
// A CtSuperAccess don't have a variable.
return true;
}
String name = variableAccess.getVariable().getSimpleName();
for (String pname : parameterNames) {
if (name.contains(pname)) {
Object value = Parameters.getValue(template, pname, null);
if ((value instanceof List) && name.equals(pname)) {
// replace list of CtParameter for generic access to the parameters
List<CtParameter<?>> l = (List<CtParameter<?>>) value;
List<CtExpression<?>> vas = factory.Code().createVariableReads(l);
CtAbstractInvocation<?> inv = (CtAbstractInvocation<?>) variableAccess.getParent();
int i = inv.getArguments().indexOf(variableAccess);
inv.getArguments().remove(i);
inv.getExecutable().getActualTypeArguments().remove(i);
for (CtExpression<?> va : vas) {
va.setParent(variableAccess.getParent());
inv.getArguments().add(i, va);
inv.getExecutable().getActualTypeArguments().add(i, va.getType());
i++;
}
throw new SkipException(variableAccess);
}
// replace variable accesses names
if (value instanceof String) {
name = name.replace(pname, (String) value);
variableAccess.getVariable().setSimpleName(name);
}
}
}
CtTypeReference<T> reference = variableAccess.getType();
if ((parameterNames != null) && (reference != null) && parameterNames.contains(reference.getSimpleName()
)) {
CtTypeReference<T> t;
Object o = Parameters.getValue(template, reference.getSimpleName(), null);
if (o instanceof Class) {
t = factory.Type().createReference(((Class<T>) o));
} else if (o instanceof CtTypeReference) {
t = (CtTypeReference<T>) o;
reference.setActualTypeArguments(t.getActualTypeArguments());
} else {
throw new RuntimeException("unsupported reference substitution");
}
variableAccess.setType(t);
}
return true;
}
}

Factory factory;
Expand Down Expand Up @@ -615,42 +464,13 @@ public void scan(Collection<? extends CtElement> elements) {

@Override
public void scan(CtElement element) {
if (element instanceof CtReference) {
try {
// doing the templating of this element
inheritanceScanner.scan(element);
if (!(element instanceof CtTypeReference)) {
// replace parameters in reference names
String name = ((CtReference) element).getSimpleName();
for (String pname : parameterNames) {
if (name.contains(pname)) {
name = name.replace(pname, Parameters.getValue(template, pname, null).toString());
((CtReference) element).setSimpleName(name);
}
}
super.scan(element);
} else {
if (!(parameterNames.contains(((CtReference) element).getSimpleName())
&& (((CtTypeReference<?>) element).getDeclaringType() != null)
&& ((CtTypeReference<?>) element).getDeclaringType().equals(templateRef))) {
super.scan(element);
}
}
} else {
try {
inheritanceScanner.scan(element);
super.scan(element);
} catch (SkipException e) {
// System.out.println(e.getMessage());
} catch (UndefinedParameterException upe) {
removeEnclosingStatement(element);
}
}
}

private void removeEnclosingStatement(CtElement e) {
if (!(e.getParent() instanceof CtBlock)) {
removeEnclosingStatement(e.getParent());
} else {
((CtStatement) e).replace(null);
// and then scan the children for doing the templating as well in them
super.scan(element);
} catch (DoNotFurtherTemplateThisElement ignore) {
}
}
}