From 0a9e2f9c4f72ea4d44537dfacb25c996d3cd3c9a Mon Sep 17 00:00:00 2001
From: Pavel Vojtechovsky
Date: Sun, 17 Sep 2017 12:48:22 +0200
Subject: [PATCH 1/2] test CtTypeReference as Template parameter
---
.../spoon/test/template/TemplateTest.java | 28 +++++++++++++
.../TypeReferenceClassAccessTemplate.java | 39 +++++++++++++++++++
2 files changed, 67 insertions(+)
create mode 100644 src/test/java/spoon/test/template/testclasses/TypeReferenceClassAccessTemplate.java
diff --git a/src/test/java/spoon/test/template/TemplateTest.java b/src/test/java/spoon/test/template/TemplateTest.java
index 4352969f112..fae101d4100 100644
--- a/src/test/java/spoon/test/template/TemplateTest.java
+++ b/src/test/java/spoon/test/template/TemplateTest.java
@@ -21,6 +21,7 @@
import spoon.reflect.declaration.CtTypeMember;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtFieldReference;
+import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.ModelConsistencyChecker;
import spoon.reflect.visitor.filter.NamedElementFilter;
import spoon.support.compiler.FileSystemFile;
@@ -44,6 +45,7 @@
import spoon.test.template.testclasses.SubStringTemplate;
import spoon.test.template.testclasses.SubstituteLiteralTemplate;
import spoon.test.template.testclasses.SubstituteRootTemplate;
+import spoon.test.template.testclasses.TypeReferenceClassAccessTemplate;
import spoon.test.template.testclasses.bounds.CheckBound;
import spoon.test.template.testclasses.bounds.CheckBoundMatcher;
import spoon.test.template.testclasses.bounds.CheckBoundTemplate;
@@ -62,6 +64,7 @@
import spoon.test.template.testclasses.types.AClassModel;
import spoon.test.template.testclasses.types.AnEnumModel;
import spoon.test.template.testclasses.types.AnIfaceModel;
+import spoon.testing.utils.ModelUtils;
import java.io.File;
import java.io.Serializable;
@@ -1014,4 +1017,29 @@ public void testAnotherFieldAccessNameSubstitution() throws Exception {
assertEquals("java.lang.System.out.println(((x) + (m_x)))", result.getAnonymousExecutables().get(0).getBody().getStatement(0).toString());
}
}
+
+ @Test
+ public void substituteTypeAccessReference() throws Exception {
+ //contract: the substitution of CtTypeAccess expression ignores actual type arguments if it have to
+ Launcher spoon = new Launcher();
+ spoon.addTemplateResource(new FileSystemFile("./src/test/java/spoon/test/template/testclasses/TypeReferenceClassAccessTemplate.java"));
+ String outputDir = "./target/spooned/test/template/testclasses";
+ spoon.setSourceOutputDirectory(outputDir);
+
+ spoon.buildModel();
+ Factory factory = spoon.getFactory();
+
+ //contract: String value is substituted in substring of literal, named element and reference
+ CtTypeReference> typeRef = factory.Type().createReference(TypeReferenceClassAccessTemplate.Example.class);
+ typeRef.addActualTypeArgument(factory.Type().DATE);
+
+ final CtClass> result = (CtClass>) new TypeReferenceClassAccessTemplate(typeRef).apply(factory.Class().create("spoon.test.template.TypeReferenceClassAccess"));
+ spoon.prettyprint();
+ ModelUtils.canBeBuilt(outputDir, 8);
+ CtMethod> method = result.getMethodsByName("someMethod").get(0);
+ assertEquals("spoon.test.template.TypeReferenceClassAccess.Example", method.getType().toString());
+ assertEquals("spoon.test.template.TypeReferenceClassAccess.Example", method.getParameters().get(0).getType().toString());
+ assertEquals("o = spoon.test.template.TypeReferenceClassAccess.Example.out", method.getBody().getStatement(0).toString());
+ assertEquals("spoon.test.template.TypeReferenceClassAccess.Example ret = new spoon.test.template.TypeReferenceClassAccess.Example()", method.getBody().getStatement(1).toString());
+ }
}
diff --git a/src/test/java/spoon/test/template/testclasses/TypeReferenceClassAccessTemplate.java b/src/test/java/spoon/test/template/testclasses/TypeReferenceClassAccessTemplate.java
new file mode 100644
index 00000000000..9970681ff0f
--- /dev/null
+++ b/src/test/java/spoon/test/template/testclasses/TypeReferenceClassAccessTemplate.java
@@ -0,0 +1,39 @@
+package spoon.test.template.testclasses;
+
+import spoon.reflect.reference.CtTypeReference;
+import spoon.template.ExtensionTemplate;
+import spoon.template.Local;
+import spoon.template.Parameter;
+
+public class TypeReferenceClassAccessTemplate extends ExtensionTemplate {
+ Object o;
+
+ $Type$ someMethod($Type$ param) {
+ o = $Type$.out;
+ $Type$ ret = new $Type$();
+ return ret;
+ }
+
+ @Local
+ public TypeReferenceClassAccessTemplate(CtTypeReference> typeRef) {
+ this.typeRef = typeRef;
+ }
+
+ @Parameter("$Type$")
+ CtTypeReference> typeRef;
+
+ @Local
+ static class $Type$ {
+ static final String out = "";
+ static long currentTimeMillis(){
+ return 0;
+ }
+ }
+
+ public static class Example {
+ static final String out = "";
+ static long currentTimeMillis(){
+ return 0;
+ }
+ }
+}
From e0b69e0f6c65ffa562ced186f9c257229cab8356 Mon Sep 17 00:00:00 2001
From: Pavel Vojtechovsky
Date: Sun, 17 Sep 2017 12:49:25 +0200
Subject: [PATCH 2/2] fix: TemplateParemeter proxy can be used for
CtTypeReference
---
.../java/spoon/template/Substitution.java | 44 ++++++++++---------
1 file changed, 23 insertions(+), 21 deletions(-)
diff --git a/src/main/java/spoon/template/Substitution.java b/src/main/java/spoon/template/Substitution.java
index d413e1b8ccc..b60f1ad6028 100644
--- a/src/main/java/spoon/template/Substitution.java
+++ b/src/main/java/spoon/template/Substitution.java
@@ -671,29 +671,31 @@ private static void checkTemplateContracts(CtClass c) {
Parameter templateParamAnnotation = f.getAnnotation(Parameter.class);
if (templateParamAnnotation != null && !templateParamAnnotation.value().equals("")) {
String proxyName = templateParamAnnotation.value();
- // contract: if value, then the field type must be String
- if (!f.getType().equals(c.getFactory().Type().STRING)) {
- throw new TemplateException("proxy template parameter must be typed as String " + f.getType().getQualifiedName());
- }
-
- // contract: the name of the template parameter must correspond to the name of the field
- // as found, by Pavel, this is not good contract because it prevents easy refactoring of templates
- // we remove it but keep th commented code in case somebody would come up with this bad idae
-// if (!f.getSimpleName().equals("_" + f.getAnnotation(Parameter.class).value())) {
-// throw new TemplateException("the field name of a proxy template parameter must be called _" + f.getSimpleName());
-// }
-
- // contract: if a proxy parameter is declared and named "x" (@Parameter("x")), then a type member named "x" must exist.
- boolean found = false;
- for (CtTypeMember member: c.getTypeMembers()) {
- if (member.getSimpleName().equals(proxyName)) {
- found = true;
+ // contract: if value, then the field type must be String or CtTypeReference
+ String fieldTypeQName = f.getType().getQualifiedName();
+ if (fieldTypeQName.equals(String.class.getName())) {
+ // contract: the name of the template parameter must correspond to the name of the field
+ // as found, by Pavel, this is not good contract because it prevents easy refactoring of templates
+ // we remove it but keep th commented code in case somebody would come up with this bad idae
+// if (!f.getSimpleName().equals("_" + f.getAnnotation(Parameter.class).value())) {
+// throw new TemplateException("the field name of a proxy template parameter must be called _" + f.getSimpleName());
+// }
+
+ // contract: if a proxy parameter is declared and named "x" (@Parameter("x")), then a type member named "x" must exist.
+ boolean found = false;
+ for (CtTypeMember member: c.getTypeMembers()) {
+ if (member.getSimpleName().equals(proxyName)) {
+ found = true;
+ }
}
+ if (!found) {
+ throw new TemplateException("if a proxy parameter is declared and named \"" + proxyName + "\", then a type member named \"\" + proxyName + \"\" must exist.");
+ }
+ } else if (fieldTypeQName.equals(CtTypeReference.class.getName())) {
+ //OK it is CtTypeReference
+ } else {
+ throw new TemplateException("proxy template parameter must be typed as String or CtTypeReference, but it is " + fieldTypeQName);
}
- if (!found) {
- throw new TemplateException("if a proxy parameter is declared and named \"" + proxyName + "\", then a type member named \"\" + proxyName + \"\" must exist.");
- }
-
}
}
}