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."); - } - } } }