From 0a684b28710bb3fe4bad69635e1bf840fba6eae3 Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Tue, 5 Dec 2017 14:28:05 +0100 Subject: [PATCH 01/13] Create new unit tests to display bug --- .../spoon/test/annotation/AnnotationTest.java | 51 +++++++++++++++++++ .../typeandfield/AnnotTypeAndField.java | 8 +++ .../testclasses/typeandfield/SimpleClass.java | 9 ++++ 3 files changed, 68 insertions(+) create mode 100644 src/test/java/spoon/test/annotation/testclasses/typeandfield/AnnotTypeAndField.java create mode 100644 src/test/java/spoon/test/annotation/testclasses/typeandfield/SimpleClass.java diff --git a/src/test/java/spoon/test/annotation/AnnotationTest.java b/src/test/java/spoon/test/annotation/AnnotationTest.java index 63ebfe2c6d9..abcf56ed06e 100644 --- a/src/test/java/spoon/test/annotation/AnnotationTest.java +++ b/src/test/java/spoon/test/annotation/AnnotationTest.java @@ -1,5 +1,6 @@ package spoon.test.annotation; +import org.apache.commons.lang3.StringUtils; import org.junit.Before; import org.junit.Test; import spoon.Launcher; @@ -66,11 +67,14 @@ import spoon.test.annotation.testclasses.repeatandarrays.RepeatedArrays; import spoon.test.annotation.testclasses.repeatandarrays.TagArrays; import spoon.test.annotation.testclasses.spring.AliasFor; +import spoon.test.annotation.testclasses.typeandfield.SimpleClass; import java.io.File; +import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -92,6 +96,7 @@ public class AnnotationTest { private Launcher launcher; private Factory factory; + @Before public void setUp() throws Exception { launcher = new Launcher(); @@ -1234,4 +1239,50 @@ public void testAnnotationNotRepeatableNotArrayAnnotation() { } } + + @Test + public void testAnnotationTypeAndFieldOnType() { + // contract: annotation on type should be written before the FQN in FQN mode + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/typeandfield"); + launcher.getEnvironment().setNoClasspath(true); + launcher.setSourceOutputDirectory("./target/spooned-typeandfield"); + launcher.run(); + + CtType type = launcher.getFactory().Type().get(SimpleClass.class); + CtField field = type.getField("anotherField"); + assertEquals(0, field.getAnnotations().size()); + + CtTypeReference typeField = field.getType(); + assertEquals(1, typeField.getAnnotations().size()); + assertEquals("java.lang.String", typeField.getQualifiedName()); + + assertEquals("@AnnotTypeAndField\njava.lang.String", typeField.toString()); + } + + @Test + public void testAnnotationTypeAndFieldOnField() throws IOException { + // contract: annotation on field with an annotation type which supports type and field, should remains on field + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/typeandfield"); + launcher.getEnvironment().setNoClasspath(true); + launcher.setSourceOutputDirectory("./target/spooned-typeandfield"); + launcher.run(); + + CtType type = launcher.getFactory().Type().get(SimpleClass.class); + + CtField field = type.getField("mandatoryField"); + assertEquals(1, field.getAnnotations().size()); + CtAnnotation annotation = field.getAnnotations().get(0); + assertEquals("spoon.test.annotation.testclasses.typeandfield.AnnotTypeAndField", annotation.getAnnotationType().getQualifiedName()); + + assertEquals("java.lang.String", field.getType().getQualifiedName()); + assertEquals(0, field.getType().getAnnotations().size()); + + List lines = Files.readAllLines(new File("./target/spooned-typeandfield/spoon/test/annotation/testclasses/typeandfield/SimpleClass.java").toPath()); + String fileContent = StringUtils.join(lines, "\n"); + + assertTrue("Content :"+fileContent, fileContent.contains("@AnnotTypeAndField")); + assertTrue("Content :"+fileContent, fileContent.contains("public java.lang.String mandatoryField;")); + } } diff --git a/src/test/java/spoon/test/annotation/testclasses/typeandfield/AnnotTypeAndField.java b/src/test/java/spoon/test/annotation/testclasses/typeandfield/AnnotTypeAndField.java new file mode 100644 index 00000000000..5cc42688399 --- /dev/null +++ b/src/test/java/spoon/test/annotation/testclasses/typeandfield/AnnotTypeAndField.java @@ -0,0 +1,8 @@ +package spoon.test.annotation.testclasses.typeandfield; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.TYPE_USE}) +public @interface AnnotTypeAndField { +} diff --git a/src/test/java/spoon/test/annotation/testclasses/typeandfield/SimpleClass.java b/src/test/java/spoon/test/annotation/testclasses/typeandfield/SimpleClass.java new file mode 100644 index 00000000000..84cd0274c7c --- /dev/null +++ b/src/test/java/spoon/test/annotation/testclasses/typeandfield/SimpleClass.java @@ -0,0 +1,9 @@ +package spoon.test.annotation.testclasses.typeandfield; + +public class SimpleClass { + + @AnnotTypeAndField + public String mandatoryField; + + public @AnnotTypeAndField String anotherField; +} From af50c5b1a704f9a09a912f9a2bb7135cc530039f Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Tue, 5 Dec 2017 14:42:50 +0100 Subject: [PATCH 02/13] Remove one test because of #520 --- .../spoon/test/annotation/AnnotationTest.java | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/test/java/spoon/test/annotation/AnnotationTest.java b/src/test/java/spoon/test/annotation/AnnotationTest.java index abcf56ed06e..88f884a575a 100644 --- a/src/test/java/spoon/test/annotation/AnnotationTest.java +++ b/src/test/java/spoon/test/annotation/AnnotationTest.java @@ -1240,26 +1240,6 @@ public void testAnnotationNotRepeatableNotArrayAnnotation() { } - @Test - public void testAnnotationTypeAndFieldOnType() { - // contract: annotation on type should be written before the FQN in FQN mode - final Launcher launcher = new Launcher(); - launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/typeandfield"); - launcher.getEnvironment().setNoClasspath(true); - launcher.setSourceOutputDirectory("./target/spooned-typeandfield"); - launcher.run(); - - CtType type = launcher.getFactory().Type().get(SimpleClass.class); - CtField field = type.getField("anotherField"); - assertEquals(0, field.getAnnotations().size()); - - CtTypeReference typeField = field.getType(); - assertEquals(1, typeField.getAnnotations().size()); - assertEquals("java.lang.String", typeField.getQualifiedName()); - - assertEquals("@AnnotTypeAndField\njava.lang.String", typeField.toString()); - } - @Test public void testAnnotationTypeAndFieldOnField() throws IOException { // contract: annotation on field with an annotation type which supports type and field, should remains on field From e9aa2e995bcc9fae02202be54ceeb33587ce5964 Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Fri, 8 Dec 2017 10:56:14 +0100 Subject: [PATCH 03/13] Change the contract of the test --- src/test/java/spoon/test/annotation/AnnotationTest.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/test/java/spoon/test/annotation/AnnotationTest.java b/src/test/java/spoon/test/annotation/AnnotationTest.java index 88f884a575a..698add9f749 100644 --- a/src/test/java/spoon/test/annotation/AnnotationTest.java +++ b/src/test/java/spoon/test/annotation/AnnotationTest.java @@ -1242,7 +1242,9 @@ public void testAnnotationNotRepeatableNotArrayAnnotation() { @Test public void testAnnotationTypeAndFieldOnField() throws IOException { - // contract: annotation on field with an annotation type which supports type and field, should remains on field + // contract: annotation on field with an annotation type which supports type and field, should be attached both on type and field + // see: https://docs.oracle.com/javase/specs/jls/se9/html/jls-9.html#jls-9.7.3 + // in this case, we want to print it only once before the type final Launcher launcher = new Launcher(); launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/typeandfield"); launcher.getEnvironment().setNoClasspath(true); @@ -1256,6 +1258,11 @@ public void testAnnotationTypeAndFieldOnField() throws IOException { CtAnnotation annotation = field.getAnnotations().get(0); assertEquals("spoon.test.annotation.testclasses.typeandfield.AnnotTypeAndField", annotation.getAnnotationType().getQualifiedName()); + CtTypeReference fieldType = field.getType(); + assertEquals(1, fieldType.getAnnotations().size()); + CtAnnotation anotherAnnotation = fieldType.getAnnotations().get(0); + assertEquals(annotation, anotherAnnotation); + assertEquals("java.lang.String", field.getType().getQualifiedName()); assertEquals(0, field.getType().getAnnotations().size()); From 718443e60e4ed9cd5afd42d51cae12425b7c7fef Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Fri, 8 Dec 2017 15:32:07 +0100 Subject: [PATCH 04/13] Fix issue and all tests --- .../reflect/declaration/CtAnnotation.java | 40 ++++ .../reflect/visitor/ElementPrinterHelper.java | 11 + .../compiler/jdt/JDTTreeBuilderQuery.java | 24 +- .../support/compiler/jdt/ParentExiter.java | 26 +- .../reflect/declaration/CtAnnotationImpl.java | 36 +-- .../spoon/test/annotation/AnnotationTest.java | 222 ++++++++++++++---- 6 files changed, 256 insertions(+), 103 deletions(-) diff --git a/src/main/java/spoon/reflect/declaration/CtAnnotation.java b/src/main/java/spoon/reflect/declaration/CtAnnotation.java index 7dd1e12e743..f16ba4a5611 100644 --- a/src/main/java/spoon/reflect/declaration/CtAnnotation.java +++ b/src/main/java/spoon/reflect/declaration/CtAnnotation.java @@ -19,7 +19,9 @@ import spoon.reflect.code.CtExpression; import spoon.reflect.code.CtFieldAccess; import spoon.reflect.code.CtLiteral; +import spoon.reflect.code.CtLocalVariable; import spoon.reflect.code.CtNewArray; +import spoon.reflect.reference.CtTypeParameterReference; import spoon.reflect.reference.CtTypeReference; import spoon.support.DerivedProperty; import spoon.reflect.annotations.PropertyGetter; @@ -165,4 +167,42 @@ public interface CtAnnotation extends CtExpression, CtS @Override @UnsettableProperty > C setTypeCasts(List> types); + + static CtAnnotatedElementType getAnnotatedElementTypeForCtElement(CtElement element) { + if (element == null) { + return null; + } + + if (element instanceof CtMethod) { + return CtAnnotatedElementType.METHOD; + } + if (element instanceof CtAnnotation || element instanceof CtAnnotationType) { + return CtAnnotatedElementType.ANNOTATION_TYPE; + } + if (element instanceof CtType) { + return CtAnnotatedElementType.TYPE; + } + if (element instanceof CtField) { + return CtAnnotatedElementType.FIELD; + } + if (element instanceof CtConstructor) { + return CtAnnotatedElementType.CONSTRUCTOR; + } + if (element instanceof CtParameter) { + return CtAnnotatedElementType.PARAMETER; + } + if (element instanceof CtLocalVariable) { + return CtAnnotatedElementType.LOCAL_VARIABLE; + } + if (element instanceof CtPackage) { + return CtAnnotatedElementType.PACKAGE; + } + if (element instanceof CtTypeParameterReference) { + return CtAnnotatedElementType.TYPE_PARAMETER; + } + if (element instanceof CtTypeReference) { + return CtAnnotatedElementType.TYPE_USE; + } + return null; + } } diff --git a/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java b/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java index e10f3fe7812..e31bf190fe5 100644 --- a/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java +++ b/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java @@ -34,7 +34,9 @@ import spoon.reflect.declaration.CtConstructor; import spoon.reflect.declaration.CtElement; import spoon.reflect.declaration.CtExecutable; +import spoon.reflect.declaration.CtField; import spoon.reflect.declaration.CtFormalTypeDeclarer; +import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.CtModifiable; import spoon.reflect.declaration.CtNamedElement; import spoon.reflect.declaration.CtParameter; @@ -42,6 +44,7 @@ import spoon.reflect.declaration.CtTypeMember; import spoon.reflect.declaration.CtTypeParameter; import spoon.reflect.declaration.ModifierKind; +import spoon.reflect.declaration.ParentNotInitializedException; import spoon.reflect.factory.Factory; import spoon.reflect.reference.CtActualTypeContainer; import spoon.reflect.reference.CtExecutableReference; @@ -77,6 +80,14 @@ public ElementPrinterHelper(TokenWriter printerTokenWriter, DefaultJavaPrettyPri */ public void writeAnnotations(CtElement element) { for (CtAnnotation annotation : element.getAnnotations()) { + try { + if (element instanceof CtTypeReference && (element.getParent() instanceof CtField || element.getParent() instanceof CtMethod) && element.getParent().getAnnotations().contains(annotation)) { + continue; + } + } catch (ParentNotInitializedException e) { + // do nothing + } + prettyPrinter.scan(annotation); printer.writeln(); } diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java index 9b97ac8bca2..ecd8e4b646c 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java @@ -37,6 +37,7 @@ import spoon.reflect.declaration.ModifierKind; import spoon.support.reflect.CtExtendedModifier; +import java.lang.reflect.Array; import java.util.HashSet; import java.util.Set; @@ -169,22 +170,33 @@ static boolean hasAnnotationWithType(Annotation a, CtAnnotatedElementType elemen if (a.resolvedType == null) { return false; } + + // if the annotation does have a @Target then we must respect its rule + // else, the annotation can be attached to any type. + boolean targetAnnotationExists = false; + for (AnnotationBinding annotation : a.resolvedType.getAnnotations()) { if (!"Target".equals(CharOperation.charToString(annotation.getAnnotationType().sourceName()))) { continue; } + targetAnnotationExists = true; Object value = annotation.getElementValuePairs()[0].value; - if (value == null || !value.getClass().isArray()) { + if (value == null) { continue; } - Object[] fields = (Object[]) value; - for (Object field : fields) { - if (field instanceof FieldBinding && elementType.name().equals(CharOperation.charToString(((FieldBinding) field).name))) { - return true; + if (value instanceof FieldBinding && elementType.name().equals(CharOperation.charToString(((FieldBinding) value).name))) { + return true; + } + if (value.getClass().isArray()) { + Object[] fields = (Object[]) value; + for (Object field : fields) { + if (field instanceof FieldBinding && elementType.name().equals(CharOperation.charToString(((FieldBinding) field).name))) { + return true; + } } } } - return false; + return !targetAnnotationExists; } /** diff --git a/src/main/java/spoon/support/compiler/jdt/ParentExiter.java b/src/main/java/spoon/support/compiler/jdt/ParentExiter.java index c56082efd0e..7b0b214bced 100644 --- a/src/main/java/spoon/support/compiler/jdt/ParentExiter.java +++ b/src/main/java/spoon/support/compiler/jdt/ParentExiter.java @@ -113,7 +113,7 @@ public class ParentExiter extends CtInheritanceScanner { private CtElement child; private ASTNode childJDT; - private Map, List>> annotationsMap = new HashMap<>(); + private Map, List> annotationsMap = new HashMap<>(); /** * @param jdtTreeBuilder @@ -133,29 +133,33 @@ public void setChild(ASTNode child) { @Override public void scanCtElement(CtElement e) { if (child instanceof CtAnnotation && this.jdtTreeBuilder.getContextBuilder().annotationValueName.isEmpty()) { - e.addAnnotation((CtAnnotation) child); + // we check if the current element can have the annotation attached + CtAnnotatedElementType annotatedElementType = CtAnnotation.getAnnotatedElementTypeForCtElement(e); + annotatedElementType = (e instanceof CtTypeParameter || e instanceof CtTypeParameterReference) ? CtAnnotatedElementType.TYPE_USE : annotatedElementType; + if (annotatedElementType != null && JDTTreeBuilderQuery.hasAnnotationWithType((Annotation) childJDT, annotatedElementType)) { + e.addAnnotation((CtAnnotation) child); + } + + // in this case the annotation should be (also) attached to the type if (e instanceof CtTypedElement && JDTTreeBuilderQuery.hasAnnotationWithType((Annotation) childJDT, CtAnnotatedElementType.TYPE_USE)) { - List> annotations = new ArrayList<>(); + List annotations = new ArrayList<>(); if (!annotationsMap.containsKey(e)) { annotationsMap.put((CtTypedElement) e, annotations); } else { annotations = annotationsMap.get(e); } - annotations.add(((CtAnnotation) child).getType()); + annotations.add((CtAnnotation) child.clone()); annotationsMap.put((CtTypedElement) e, annotations); } - return; } } private void substituteAnnotation(CtTypedElement ele) { if (annotationsMap.containsKey(ele)) { - List> annotations = annotationsMap.get(ele); - for (CtTypeReference annotation : annotations) { - final CtAnnotation targetAnnotation = ele.getAnnotation(annotation); - ele.removeAnnotation(targetAnnotation); - if (!ele.getType().getAnnotations().contains(targetAnnotation)) { - ele.getType().addAnnotation(targetAnnotation); + List annotations = annotationsMap.get(ele); + for (CtAnnotation annotation : annotations) { + if (!ele.getType().getAnnotations().contains(annotation)) { + ele.getType().addAnnotation(annotation); } } annotationsMap.remove(ele); diff --git a/src/main/java/spoon/support/reflect/declaration/CtAnnotationImpl.java b/src/main/java/spoon/support/reflect/declaration/CtAnnotationImpl.java index 54cc4616f95..fcc90efbca7 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtAnnotationImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtAnnotationImpl.java @@ -425,41 +425,7 @@ public CtElement getAnnotatedElement() { public CtAnnotatedElementType getAnnotatedElementType() { CtElement annotatedElement = this.getAnnotatedElement(); - if (annotatedElement == null) { - return null; - } - - if (annotatedElement instanceof CtMethod) { - return CtAnnotatedElementType.METHOD; - } - if (annotatedElement instanceof CtAnnotation || annotatedElement instanceof CtAnnotationType) { - return CtAnnotatedElementType.ANNOTATION_TYPE; - } - if (annotatedElement instanceof CtType) { - return CtAnnotatedElementType.TYPE; - } - if (annotatedElement instanceof CtField) { - return CtAnnotatedElementType.FIELD; - } - if (annotatedElement instanceof CtConstructor) { - return CtAnnotatedElementType.CONSTRUCTOR; - } - if (annotatedElement instanceof CtParameter) { - return CtAnnotatedElementType.PARAMETER; - } - if (annotatedElement instanceof CtLocalVariable) { - return CtAnnotatedElementType.LOCAL_VARIABLE; - } - if (annotatedElement instanceof CtPackage) { - return CtAnnotatedElementType.PACKAGE; - } - if (annotatedElement instanceof CtTypeParameterReference) { - return CtAnnotatedElementType.TYPE_PARAMETER; - } - if (annotatedElement instanceof CtTypeReference) { - return CtAnnotatedElementType.TYPE_USE; - } - return null; + return CtAnnotation.getAnnotatedElementTypeForCtElement(annotatedElement); } @SuppressWarnings("unchecked") diff --git a/src/test/java/spoon/test/annotation/AnnotationTest.java b/src/test/java/spoon/test/annotation/AnnotationTest.java index 698add9f749..ff46b3702e1 100644 --- a/src/test/java/spoon/test/annotation/AnnotationTest.java +++ b/src/test/java/spoon/test/annotation/AnnotationTest.java @@ -94,18 +94,6 @@ import static spoon.testing.utils.ModelUtils.canBeBuilt; public class AnnotationTest { - private Launcher launcher; - private Factory factory; - - @Before - public void setUp() throws Exception { - launcher = new Launcher(); - launcher.run(new String[] { - "-i", "./src/test/java/spoon/test/annotation/testclasses/", - "--output-type", "nooutput" - }); - factory = launcher.getFactory(); - } @Test public void testAnnotationValueReflection() throws Exception { @@ -120,21 +108,33 @@ public void testAnnotationValueReflection() throws Exception { @Test public void testModelBuildingAnnotationBound() throws Exception { - CtType type = this.factory.Type().get("spoon.test.annotation.testclasses.Bound"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/Bound.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + CtType type = factory.Type().get("spoon.test.annotation.testclasses.Bound"); assertEquals("Bound", type.getSimpleName()); assertEquals(1, type.getAnnotations().size()); } @Test public void testWritingAnnotParamArray() throws Exception { - CtType type = this.factory.Type().get("spoon.test.annotation.testclasses.AnnotParam"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotParam.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + CtType type = factory.Type().get("spoon.test.annotation.testclasses.AnnotParam"); assertEquals("@java.lang.SuppressWarnings({ \"unused\", \"rawtypes\" })", type.getElements(new TypeFilter<>(CtAnnotation.class)).get(0).toString()); } @Test public void testModelBuildingAnnotationBoundUsage() throws Exception { - CtType type = this.factory.Type().get("spoon.test.annotation.testclasses.Main"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/Main.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + CtType type = factory.Type().get("spoon.test.annotation.testclasses.Main"); assertEquals("Main", type.getSimpleName()); CtParameter param = type.getElements(new TypeFilter>(CtParameter.class)).get(0); @@ -150,7 +150,11 @@ public void testModelBuildingAnnotationBoundUsage() throws Exception { @Test public void testPersistenceProperty() throws Exception { - CtType type = this.factory.Type().get("spoon.test.annotation.testclasses.PersistenceProperty"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/PersistenceProperty.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + CtType type = factory.Type().get("spoon.test.annotation.testclasses.PersistenceProperty"); assertEquals("PersistenceProperty", type.getSimpleName()); assertEquals(2, type.getAnnotations().size()); @@ -166,7 +170,11 @@ public void testPersistenceProperty() throws Exception { @Test public void testAnnotationParameterTypes() throws Exception { - CtType type = this.factory.Type().get("spoon.test.annotation.testclasses.Main"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/Main.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + CtType type = factory.Type().get("spoon.test.annotation.testclasses.Main"); CtMethod m1 = type.getElements(new NamedElementFilter<>(CtMethod.class, "m1")).get(0); @@ -247,8 +255,12 @@ public void testAnnotationParameterTypes() throws Exception { @Test public void testAnnotatedElementTypes() throws Exception { + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); // load package of the test classes - CtPackage pkg = this.factory.Package().get("spoon.test.annotation.testclasses"); + CtPackage pkg = factory.Package().get("spoon.test.annotation.testclasses"); // check annotated element type of the package annotation List> annotations = pkg.getAnnotations(); @@ -363,15 +375,20 @@ public void testAnnotatedElementTypes() throws Exception { @Test public void testAnnotationWithDefaultArrayValue() throws Throwable { + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotArrayInnerClass.java"); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotArray.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); final String res = "java.lang.Class[] value() default { };"; - CtType type = this.factory.Type().get("spoon.test.annotation.testclasses.AnnotArrayInnerClass"); + CtType type = factory.Type().get("spoon.test.annotation.testclasses.AnnotArrayInnerClass"); CtType annotationInnerClass = type.getNestedType("Annotation"); assertEquals("Annotation", annotationInnerClass.getSimpleName()); assertEquals(1, annotationInnerClass.getAnnotations().size()); assertEquals(res, annotationInnerClass.getMethod("value").toString()); - CtType annotation = this.factory.Type().get("spoon.test.annotation.testclasses.AnnotArray"); + CtType annotation = factory.Type().get("spoon.test.annotation.testclasses.AnnotArray"); assertEquals("AnnotArray", annotation.getSimpleName()); assertEquals(1, annotation.getAnnotations().size()); assertEquals(res, annotation.getMethod("value").toString()); @@ -379,7 +396,11 @@ public void testAnnotationWithDefaultArrayValue() throws Throwable { @Test public void testInnerAnnotationsWithArray() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get("spoon.test.annotation.testclasses.Foo"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/Foo.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get("spoon.test.annotation.testclasses.Foo"); final CtMethod testMethod = ctClass.getMethodsByName("test").get(0); final List> testMethodAnnotations = testMethod.getAnnotations(); assertEquals(1, testMethodAnnotations.size()); @@ -407,7 +428,11 @@ public void testInnerAnnotationsWithArray() throws Exception { @Test public void testAccessAnnotationValue() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get("spoon.test.annotation.testclasses.Main"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/Main.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get("spoon.test.annotation.testclasses.Main"); CtMethod testMethod = ctClass.getMethodsByName("testValueWithArray").get(0); Class[] value = testMethod.getAnnotation(AnnotArray.class).value(); assertArrayEquals(new Class[] { RuntimeException.class }, value); @@ -419,7 +444,11 @@ public void testAccessAnnotationValue() throws Exception { @Test public void testUsageOfTypeAnnotationInNewInstance() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsAppliedOnAnyTypeInAClass.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); final CtConstructorCall ctConstructorCall = ctClass.getElements(new AbstractFilter>(CtConstructorCall.class) { @Override @@ -437,7 +466,11 @@ public boolean matches(CtConstructorCall element) { @Test public void testUsageOfTypeAnnotationInCast() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsAppliedOnAnyTypeInAClass.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); final CtReturn returns = ctClass.getElements(new AbstractFilter>(CtReturn.class) { @Override @@ -456,7 +489,11 @@ public boolean matches(CtReturn element) { @Test public void testUsageOfTypeAnnotationBeforeExceptionInSignatureOfMethod() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsAppliedOnAnyTypeInAClass.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); final CtMethod method = ctClass.getMethodsByName("m").get(0); final CtTypeReference thrownReference = method.getThrownTypes().toArray(new CtTypeReference[0])[0]; @@ -471,7 +508,11 @@ public void testUsageOfTypeAnnotationBeforeExceptionInSignatureOfMethod() throws @Test public void testUsageOfTypeAnnotationInReturnTypeInMethod() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsAppliedOnAnyTypeInAClass.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); final CtMethod method = ctClass.getMethodsByName("m3").get(0); final List> typeAnnotations = method.getType().getAnnotations(); @@ -487,7 +528,11 @@ public void testUsageOfTypeAnnotationInReturnTypeInMethod() throws Exception { @Test public void testUsageOfTypeAnnotationOnParameterInMethod() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get(AnnotationsAppliedOnAnyTypeInAClass.class); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsAppliedOnAnyTypeInAClass.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get(AnnotationsAppliedOnAnyTypeInAClass.class); final CtMethod method = ctClass.getMethodsByName("m6").get(0); final CtParameter ctParameter = method.getParameters().get(0); @@ -501,7 +546,11 @@ public void testUsageOfTypeAnnotationOnParameterInMethod() throws Exception { @Test public void testUsageOfTypeAnnotationOnLocalVariableInMethod() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get(AnnotationsAppliedOnAnyTypeInAClass.class); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsAppliedOnAnyTypeInAClass.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get(AnnotationsAppliedOnAnyTypeInAClass.class); final CtMethod method = ctClass.getMethodsByName("m6").get(0); final CtLocalVariable ctLocalVariable = method.getBody().getElements(new AbstractFilter>(CtLocalVariable.class) { @@ -520,7 +569,11 @@ public boolean matches(CtLocalVariable element) { @Test public void testUsageOfTypeAnnotationInExtendsImplementsOfAClass() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsAppliedOnAnyTypeInAClass.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); final CtClass innerClass = ctClass.getElements(new NamedElementFilter<>(CtClass.class,"DummyClass")).get(0); final CtTypeReference extendsActual = innerClass.getSuperclass(); @@ -563,7 +616,11 @@ public void testUsageOfTypeAnnotationInExtendsImplementsOfAClass() throws Except @Test public void testUsageOfTypeAnnotationWithGenericTypesInClassDeclaration() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsAppliedOnAnyTypeInAClass.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); final CtClass genericClass = ctClass.getElements(new NamedElementFilter<>(CtClass.class,"DummyGenericClass")).get(0); @@ -580,7 +637,11 @@ public void testUsageOfTypeAnnotationWithGenericTypesInClassDeclaration() throws @Test public void testUsageOfTypeAnnotationWithGenericTypesInStatements() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsAppliedOnAnyTypeInAClass.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); final CtMethod method = ctClass.getMethodsByName("m4").get(0); @@ -626,7 +687,11 @@ public void testUsageOfTypeAnnotationWithGenericTypesInStatements() throws Excep @Test public void testUsageOfParametersInTypeAnnotation() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsAppliedOnAnyTypeInAClass.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get("spoon.test.annotation.testclasses.AnnotationsAppliedOnAnyTypeInAClass"); final CtMethod method = ctClass.getMethodsByName("m5").get(0); final String integerParam = "java.util.List<@spoon.test.annotation.testclasses.TypeAnnotation(integer = 1)" + System.lineSeparator() + "T> list"; @@ -665,15 +730,23 @@ public void testUsageOfParametersInTypeAnnotation() throws Exception { @Test public void testOutputGeneratedByTypeAnnotation() throws Exception { + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsAppliedOnAnyTypeInAClass.java"); + launcher.buildModel(); // we only write to disk here launcher.setSourceOutputDirectory(new File("./target/spooned/")); launcher.getModelBuilder().generateProcessedSourceFiles(OutputType.CLASSES); + canBeBuilt(new File("./target/spooned/spoon/test/annotation/testclasses/"), 8); } @Test public void testRepeatSameAnnotationOnClass() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get(AnnotationsRepeated.class); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsRepeated.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get(AnnotationsRepeated.class); final List> annotations = ctClass.getAnnotations(); assertEquals("Class must to have multi annotation of the same type", 2, annotations.size()); @@ -685,7 +758,11 @@ public void testRepeatSameAnnotationOnClass() throws Exception { @Test public void testRepeatSameAnnotationOnField() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get(AnnotationsRepeated.class); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsRepeated.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get(AnnotationsRepeated.class); final CtField field = ctClass.getField("field"); final List> annotations = field.getAnnotations(); @@ -698,7 +775,11 @@ public void testRepeatSameAnnotationOnField() throws Exception { @Test public void testRepeatSameAnnotationOnMethod() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get(AnnotationsRepeated.class); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsRepeated.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get(AnnotationsRepeated.class); final CtMethod method = ctClass.getMethodsByName("method").get(0); final List> annotations = method.getAnnotations(); @@ -711,7 +792,11 @@ public void testRepeatSameAnnotationOnMethod() throws Exception { @Test public void testRepeatSameAnnotationOnConstructor() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get(AnnotationsRepeated.class); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsRepeated.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get(AnnotationsRepeated.class); final CtConstructor ctConstructor = ctClass.getConstructors().toArray(new CtConstructor[0])[0]; final List> annotations = ctConstructor.getAnnotations(); @@ -724,7 +809,11 @@ public void testRepeatSameAnnotationOnConstructor() throws Exception { @Test public void testRepeatSameAnnotationOnParameter() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get(AnnotationsRepeated.class); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsRepeated.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get(AnnotationsRepeated.class); final CtMethod method = ctClass.getMethodsByName("methodWithParameter").get(0); final CtParameter ctParameter = method.getParameters().get(0); @@ -738,7 +827,11 @@ public void testRepeatSameAnnotationOnParameter() throws Exception { @Test public void testRepeatSameAnnotationOnLocalVariable() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get(AnnotationsRepeated.class); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsRepeated.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get(AnnotationsRepeated.class); final CtMethod method = ctClass.getMethodsByName("methodWithLocalVariable").get(0); final CtLocalVariable ctLocalVariable = method.getBody().getElements(new AbstractFilter>(CtLocalVariable.class) { @Override @@ -757,7 +850,12 @@ public boolean matches(CtLocalVariable element) { @Test public void testRepeatSameAnnotationOnPackage() throws Exception { - final CtPackage pkg = this.factory.Package().get("spoon.test.annotation.testclasses"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsRepeated.java"); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/package-info.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtPackage pkg = factory.Package().get("spoon.test.annotation.testclasses"); final List> annotations = pkg.getAnnotations(); assertEquals("Local variable must to have multi annotation of the same type", 2, annotations.size()); @@ -769,6 +867,10 @@ public void testRepeatSameAnnotationOnPackage() throws Exception { @Test public void testDefaultValueInAnnotationsForAnnotationFields() throws Exception { + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationDefaultAnnotation.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); final CtType annotation = factory.Type().get(AnnotationDefaultAnnotation.class); final CtAnnotationMethod ctAnnotations = annotation.getMethods().toArray(new CtAnnotationMethod[0])[0]; @@ -779,7 +881,11 @@ public void testDefaultValueInAnnotationsForAnnotationFields() throws Exception @Test public void testGetAnnotationOuter() throws Exception { - final CtClass ctClass = (CtClass) this.factory.Type().get("spoon.test.annotation.testclasses.Foo"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/Foo.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtClass ctClass = (CtClass) factory.Type().get("spoon.test.annotation.testclasses.Foo"); final CtMethod testMethod = ctClass.getMethodsByName("test").get(0); Foo.OuterAnnotation annot = testMethod.getAnnotation(Foo.OuterAnnotation.class); assertNotNull(annot); @@ -797,7 +903,7 @@ public void testAbstractAllAnnotationProcessor() throws Exception { spoon.addInputResource("./src/test/java/spoon/test/annotation/testclasses/Inception.java"); spoon.addInputResource("./src/test/java/spoon/test/annotation/testclasses/TestAnnotation.java"); spoon.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotArrayInnerClass.java"); - factory = spoon.getFactory(); + Factory factory = spoon.getFactory(); spoon.buildModel(); // create the processor @@ -819,7 +925,7 @@ public void testAbstractAllAnnotationProcessorWithGlobalAnnotation() throws Exce spoon.addInputResource("./src/test/java/spoon/test/annotation/testclasses/Inception.java"); spoon.addInputResource("./src/test/java/spoon/test/annotation/testclasses/GlobalAnnotation.java"); spoon.addInputResource("./src/test/java/spoon/test/annotation/testclasses/TestAnnotation.java"); - factory = spoon.getFactory(); + Factory factory = spoon.getFactory(); spoon.buildModel(); // create the processor @@ -830,12 +936,16 @@ public void testAbstractAllAnnotationProcessorWithGlobalAnnotation() throws Exce p.addProcessor(methodProcessor); p.process(factory.Class().getAll()); - assertEquals(5, processor.elements.size()); + assertEquals(7, processor.elements.size()); // GlobalAnnotation is also attached to the type assertEquals(2, methodProcessor.elements.size()); } @Test public void testAnnotationIntrospection() throws Exception { + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationIntrospection.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); CtClass aClass = factory.Class().get(AnnotationIntrospection.class); CtMethod mMethod = aClass.getMethod("m"); CtStatement statement = mMethod.getBody().getStatement(1); @@ -862,7 +972,11 @@ public void testFieldAndMethodInAnnotation() throws Exception { @Test public void testAnnotationInterfacePreserveMethods() throws Exception { - final CtAnnotationType ctAnnotationType = (CtAnnotationType) this.factory.Type().get(PortRange.class); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/PortRange.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + final CtAnnotationType ctAnnotationType = (CtAnnotationType) factory.Type().get(PortRange.class); List> ctMethodMin = ctAnnotationType.getMethodsByName("min"); assertEquals("Method min is preserved after transformation", 1, ctMethodMin.size()); @@ -933,7 +1047,7 @@ public void testSpoonSpoonResult() throws Exception { spoon.addInputResource("./src/test/java/spoon/test/annotation/testclasses/dropwizard/GraphiteReporterFactory.java"); String output = "target/spooned-" + this.getClass().getSimpleName()+"-firstspoon/"; spoon.setSourceOutputDirectory(output); - factory = spoon.getFactory(); + Factory factory = spoon.getFactory(); spoon.run(); Launcher spoon2 = new Launcher(); @@ -965,7 +1079,7 @@ public void testGetAnnotationFromParameter() { String output = "target/spooned-" + this.getClass().getSimpleName() + "-firstspoon/"; spoon.setSourceOutputDirectory(output); spoon.getEnvironment().setNoClasspath(true); - factory = spoon.getFactory(); + Factory factory = spoon.getFactory(); spoon.buildModel(); List methods = factory.getModel().getElements(new NamedElementFilter<>(CtMethod.class, "setField")); @@ -1002,7 +1116,7 @@ public void annotationAddValue() { spoon.addInputResource("./src/test/java/spoon/test/annotation/testclasses/Bar.java"); spoon.buildModel(); - factory = spoon.getFactory(); + Factory factory = spoon.getFactory(); List methods = factory.getModel().getElements(new NamedElementFilter(CtMethod.class, "bidule")); @@ -1018,7 +1132,7 @@ public void annotationAddValue() { @Test public void annotationOverrideFQNIsOK() { Launcher spoon = new Launcher(); - factory = spoon.getFactory(); + Factory factory = spoon.getFactory(); factory.getEnvironment().setNoClasspath(true); spoon.addInputResource("./src/test/resources/noclasspath/annotation/issue1307/SpecIterator.java"); spoon.buildModel(); @@ -1037,6 +1151,8 @@ public void annotationOverrideFQNIsOK() { @Test public void testCreateAnnotation() throws Exception { + final Launcher launcher = new Launcher(); + Factory factory = launcher.getFactory(); CtType type = factory.Annotation().create("spoon.test.annotation.testclasses.NewAnnot"); assertTrue(type.isAnnotationType()); assertSame(type, type.getReference().getDeclaration()); @@ -1044,7 +1160,11 @@ public void testCreateAnnotation() throws Exception { @Test public void testReplaceAnnotationValue() throws Exception { - CtType type = this.factory.Type().get("spoon.test.annotation.testclasses.Main"); + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/Main.java"); + launcher.buildModel(); + Factory factory = launcher.getFactory(); + CtType type = factory.Type().get("spoon.test.annotation.testclasses.Main"); CtMethod m1 = type.getElements(new NamedElementFilter<>(CtMethod.class,"m1")).get(0); @@ -1264,12 +1384,12 @@ public void testAnnotationTypeAndFieldOnField() throws IOException { assertEquals(annotation, anotherAnnotation); assertEquals("java.lang.String", field.getType().getQualifiedName()); - assertEquals(0, field.getType().getAnnotations().size()); + assertEquals(1, field.getType().getAnnotations().size()); List lines = Files.readAllLines(new File("./target/spooned-typeandfield/spoon/test/annotation/testclasses/typeandfield/SimpleClass.java").toPath()); String fileContent = StringUtils.join(lines, "\n"); - assertTrue("Content :"+fileContent, fileContent.contains("@AnnotTypeAndField")); + assertTrue("Content :"+fileContent, fileContent.contains("@spoon.test.annotation.testclasses.typeandfield.AnnotTypeAndField")); assertTrue("Content :"+fileContent, fileContent.contains("public java.lang.String mandatoryField;")); } } From 153f1e5407de0db7eba8246d1e6ff3516b3d6237 Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Fri, 8 Dec 2017 15:34:05 +0100 Subject: [PATCH 05/13] Fix checkstyle --- .../java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java | 1 - .../spoon/support/reflect/declaration/CtAnnotationImpl.java | 5 ----- 2 files changed, 6 deletions(-) diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java index ecd8e4b646c..dcdae24f8cf 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java @@ -37,7 +37,6 @@ import spoon.reflect.declaration.ModifierKind; import spoon.support.reflect.CtExtendedModifier; -import java.lang.reflect.Array; import java.util.HashSet; import java.util.Set; diff --git a/src/main/java/spoon/support/reflect/declaration/CtAnnotationImpl.java b/src/main/java/spoon/support/reflect/declaration/CtAnnotationImpl.java index fcc90efbca7..dc31cd4552d 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtAnnotationImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtAnnotationImpl.java @@ -24,25 +24,20 @@ import spoon.reflect.code.CtFieldAccess; import spoon.reflect.code.CtFieldRead; import spoon.reflect.code.CtLiteral; -import spoon.reflect.code.CtLocalVariable; import spoon.reflect.code.CtNewArray; import spoon.reflect.code.CtTypeAccess; import spoon.reflect.declaration.CtAnnotatedElementType; import spoon.reflect.declaration.CtAnnotation; import spoon.reflect.declaration.CtAnnotationMethod; import spoon.reflect.declaration.CtAnnotationType; -import spoon.reflect.declaration.CtConstructor; import spoon.reflect.declaration.CtElement; import spoon.reflect.declaration.CtField; import spoon.reflect.declaration.CtMethod; -import spoon.reflect.declaration.CtPackage; -import spoon.reflect.declaration.CtParameter; import spoon.reflect.declaration.CtShadowable; import spoon.reflect.declaration.CtType; import spoon.reflect.eval.PartialEvaluator; import spoon.reflect.path.CtRole; import spoon.reflect.reference.CtFieldReference; -import spoon.reflect.reference.CtTypeParameterReference; import spoon.reflect.reference.CtTypeReference; import spoon.reflect.visitor.CtVisitor; import spoon.support.DerivedProperty; From 855c50532e17bae7be0f13a14803827a7873ac45 Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Fri, 8 Dec 2017 15:54:57 +0100 Subject: [PATCH 06/13] Fix tests --- src/test/java/spoon/test/type/testclasses/Spice.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/spoon/test/type/testclasses/Spice.java b/src/test/java/spoon/test/type/testclasses/Spice.java index 668ee49aa98..7e9c9993387 100644 --- a/src/test/java/spoon/test/type/testclasses/Spice.java +++ b/src/test/java/spoon/test/type/testclasses/Spice.java @@ -17,6 +17,10 @@ package spoon.test.type.testclasses; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.FIELD}) public @interface Spice { Class klass(); } From 19a26ba0184e1bb10220a21832a425f18322f3e0 Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Fri, 8 Dec 2017 16:50:07 +0100 Subject: [PATCH 07/13] Fix corner case --- .../support/compiler/jdt/JDTTreeBuilderQuery.java | 10 +++++++--- .../spoon/support/compiler/jdt/ParentExiter.java | 14 ++++++++++++-- .../java/spoon/test/type/testclasses/Spice.java | 4 ---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java index dcdae24f8cf..adcdc6cf891 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java @@ -170,8 +170,10 @@ static boolean hasAnnotationWithType(Annotation a, CtAnnotatedElementType elemen return false; } - // if the annotation does have a @Target then we must respect its rule - // else, the annotation can be attached to any type. + // JLS says: + // "If an annotation of type java.lang.annotation.Target is not present on the declaration of an annotation type T, + // then T is applicable in all declaration contexts except type parameter declarations, and in no type contexts." + boolean shouldTargetAnnotationExists = (elementType == CtAnnotatedElementType.TYPE_USE || elementType == CtAnnotatedElementType.TYPE_PARAMETER); boolean targetAnnotationExists = false; for (AnnotationBinding annotation : a.resolvedType.getAnnotations()) { @@ -195,7 +197,9 @@ static boolean hasAnnotationWithType(Annotation a, CtAnnotatedElementType elemen } } } - return !targetAnnotationExists; + + // true here means that the target annotation is not mandatory and we don't have found it + return !shouldTargetAnnotationExists && !targetAnnotationExists; } /** diff --git a/src/main/java/spoon/support/compiler/jdt/ParentExiter.java b/src/main/java/spoon/support/compiler/jdt/ParentExiter.java index 7b0b214bced..d8a3658075f 100644 --- a/src/main/java/spoon/support/compiler/jdt/ParentExiter.java +++ b/src/main/java/spoon/support/compiler/jdt/ParentExiter.java @@ -136,7 +136,9 @@ public void scanCtElement(CtElement e) { // we check if the current element can have the annotation attached CtAnnotatedElementType annotatedElementType = CtAnnotation.getAnnotatedElementTypeForCtElement(e); annotatedElementType = (e instanceof CtTypeParameter || e instanceof CtTypeParameterReference) ? CtAnnotatedElementType.TYPE_USE : annotatedElementType; - if (annotatedElementType != null && JDTTreeBuilderQuery.hasAnnotationWithType((Annotation) childJDT, annotatedElementType)) { + + // in case of noclasspath, we can be 100% sure, so we guess it must be attached... + if (this.jdtTreeBuilder.getFactory().getEnvironment().getNoClasspath() || (annotatedElementType != null && JDTTreeBuilderQuery.hasAnnotationWithType((Annotation) childJDT, annotatedElementType))) { e.addAnnotation((CtAnnotation) child); } @@ -158,8 +160,16 @@ private void substituteAnnotation(CtTypedElement ele) { if (annotationsMap.containsKey(ele)) { List annotations = annotationsMap.get(ele); for (CtAnnotation annotation : annotations) { + + // in case of noclasspath we attached previously the element: + // if we are here, we may have find an element for whom it's a better place + if (this.jdtTreeBuilder.getFactory().getEnvironment().getNoClasspath() && annotation.isParentInitialized()) { + CtElement parent = annotation.getParent(); + parent.removeAnnotation(annotation); + } + if (!ele.getType().getAnnotations().contains(annotation)) { - ele.getType().addAnnotation(annotation); + ele.getType().addAnnotation(annotation.clone()); } } annotationsMap.remove(ele); diff --git a/src/test/java/spoon/test/type/testclasses/Spice.java b/src/test/java/spoon/test/type/testclasses/Spice.java index 7e9c9993387..668ee49aa98 100644 --- a/src/test/java/spoon/test/type/testclasses/Spice.java +++ b/src/test/java/spoon/test/type/testclasses/Spice.java @@ -17,10 +17,6 @@ package spoon.test.type.testclasses; -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; - -@Target({ElementType.METHOD, ElementType.FIELD}) public @interface Spice { Class klass(); } From d49f7637c5110886d6acb4ca4cdb014e706bbf3d Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Fri, 8 Dec 2017 16:53:05 +0100 Subject: [PATCH 08/13] Remove ugly try catch --- .../java/spoon/reflect/visitor/ElementPrinterHelper.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java b/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java index e31bf190fe5..e04fe3ba340 100644 --- a/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java +++ b/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java @@ -80,12 +80,8 @@ public ElementPrinterHelper(TokenWriter printerTokenWriter, DefaultJavaPrettyPri */ public void writeAnnotations(CtElement element) { for (CtAnnotation annotation : element.getAnnotations()) { - try { - if (element instanceof CtTypeReference && (element.getParent() instanceof CtField || element.getParent() instanceof CtMethod) && element.getParent().getAnnotations().contains(annotation)) { + if (element.isParentInitialized() && element instanceof CtTypeReference && (element.getParent() instanceof CtField || element.getParent() instanceof CtMethod) && element.getParent().getAnnotations().contains(annotation)) { continue; - } - } catch (ParentNotInitializedException e) { - // do nothing } prettyPrinter.scan(annotation); From 28871ad20b0a6391934f4e61a02071f6b74ff364 Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Fri, 8 Dec 2017 16:53:56 +0100 Subject: [PATCH 09/13] Fix checkstyle --- src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java b/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java index e04fe3ba340..d2e5db5f5df 100644 --- a/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java +++ b/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java @@ -44,7 +44,6 @@ import spoon.reflect.declaration.CtTypeMember; import spoon.reflect.declaration.CtTypeParameter; import spoon.reflect.declaration.ModifierKind; -import spoon.reflect.declaration.ParentNotInitializedException; import spoon.reflect.factory.Factory; import spoon.reflect.reference.CtActualTypeContainer; import spoon.reflect.reference.CtExecutableReference; From 74a9411dd0482a538a780bc4990942b067b5a9d7 Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Fri, 8 Dec 2017 17:12:48 +0100 Subject: [PATCH 10/13] Try to understand the test error --- src/main/java/spoon/testing/utils/ModelUtils.java | 2 +- src/test/java/spoon/test/annotation/AnnotationTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/spoon/testing/utils/ModelUtils.java b/src/main/java/spoon/testing/utils/ModelUtils.java index 8f12808746d..39db806bb49 100644 --- a/src/main/java/spoon/testing/utils/ModelUtils.java +++ b/src/main/java/spoon/testing/utils/ModelUtils.java @@ -114,7 +114,7 @@ public static void canBeBuilt(File outputDirectoryFile, int complianceLevel, boo try { compiler.build(); } catch (Exception e) { - final AssertionError error = new AssertionError("Can't compile " + outputDirectoryFile.getName()); + final AssertionError error = new AssertionError("Can't compile " + outputDirectoryFile.getName()+" because "+e.getMessage()); error.initCause(e); throw error; } diff --git a/src/test/java/spoon/test/annotation/AnnotationTest.java b/src/test/java/spoon/test/annotation/AnnotationTest.java index ff46b3702e1..62dac26258f 100644 --- a/src/test/java/spoon/test/annotation/AnnotationTest.java +++ b/src/test/java/spoon/test/annotation/AnnotationTest.java @@ -734,10 +734,10 @@ public void testOutputGeneratedByTypeAnnotation() throws Exception { launcher.addInputResource("./src/test/java/spoon/test/annotation/testclasses/AnnotationsAppliedOnAnyTypeInAClass.java"); launcher.buildModel(); // we only write to disk here - launcher.setSourceOutputDirectory(new File("./target/spooned/")); + launcher.setSourceOutputDirectory(new File("./target/spooned-annotation-output/")); launcher.getModelBuilder().generateProcessedSourceFiles(OutputType.CLASSES); - canBeBuilt(new File("./target/spooned/spoon/test/annotation/testclasses/"), 8); + canBeBuilt(new File("./target/spooned-annotation-output/spoon/test/annotation/testclasses/"), 8); } @Test From b353b9fa01b06eba0a6f97d5f4cc9a40711c673d Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Fri, 8 Dec 2017 17:24:54 +0100 Subject: [PATCH 11/13] Fix checkstyle and element printer helper --- src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java | 3 ++- src/main/java/spoon/testing/utils/ModelUtils.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java b/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java index d2e5db5f5df..e90fe5b888f 100644 --- a/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java +++ b/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java @@ -43,6 +43,7 @@ import spoon.reflect.declaration.CtType; import spoon.reflect.declaration.CtTypeMember; import spoon.reflect.declaration.CtTypeParameter; +import spoon.reflect.declaration.CtTypedElement; import spoon.reflect.declaration.ModifierKind; import spoon.reflect.factory.Factory; import spoon.reflect.reference.CtActualTypeContainer; @@ -79,7 +80,7 @@ public ElementPrinterHelper(TokenWriter printerTokenWriter, DefaultJavaPrettyPri */ public void writeAnnotations(CtElement element) { for (CtAnnotation annotation : element.getAnnotations()) { - if (element.isParentInitialized() && element instanceof CtTypeReference && (element.getParent() instanceof CtField || element.getParent() instanceof CtMethod) && element.getParent().getAnnotations().contains(annotation)) { + if (element.isParentInitialized() && element instanceof CtTypeReference && (element.getParent() instanceof CtTypedElement) && element.getParent().getAnnotations().contains(annotation)) { continue; } diff --git a/src/main/java/spoon/testing/utils/ModelUtils.java b/src/main/java/spoon/testing/utils/ModelUtils.java index 39db806bb49..8daa78b017c 100644 --- a/src/main/java/spoon/testing/utils/ModelUtils.java +++ b/src/main/java/spoon/testing/utils/ModelUtils.java @@ -114,7 +114,7 @@ public static void canBeBuilt(File outputDirectoryFile, int complianceLevel, boo try { compiler.build(); } catch (Exception e) { - final AssertionError error = new AssertionError("Can't compile " + outputDirectoryFile.getName()+" because "+e.getMessage()); + final AssertionError error = new AssertionError("Can't compile " + outputDirectoryFile.getName() + " because " + e.getMessage()); error.initCause(e); throw error; } From 92eb3f0f5219b34bef363ff76a5ea0914cb6b2a9 Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Fri, 8 Dec 2017 17:28:29 +0100 Subject: [PATCH 12/13] Fix checkstyle! --- src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java b/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java index e90fe5b888f..123da46a55b 100644 --- a/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java +++ b/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java @@ -34,9 +34,7 @@ import spoon.reflect.declaration.CtConstructor; import spoon.reflect.declaration.CtElement; import spoon.reflect.declaration.CtExecutable; -import spoon.reflect.declaration.CtField; import spoon.reflect.declaration.CtFormalTypeDeclarer; -import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.CtModifiable; import spoon.reflect.declaration.CtNamedElement; import spoon.reflect.declaration.CtParameter; From 87115d5854465e20600053d25a2d8be8628ac074 Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Mon, 11 Dec 2017 09:58:05 +0100 Subject: [PATCH 13/13] Fix comments --- .../java/spoon/reflect/visitor/ElementPrinterHelper.java | 6 ++++++ .../spoon/support/compiler/jdt/JDTTreeBuilderQuery.java | 2 +- src/main/java/spoon/support/compiler/jdt/ParentExiter.java | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java b/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java index 123da46a55b..ac112382cf5 100644 --- a/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java +++ b/src/main/java/spoon/reflect/visitor/ElementPrinterHelper.java @@ -78,6 +78,12 @@ public ElementPrinterHelper(TokenWriter printerTokenWriter, DefaultJavaPrettyPri */ public void writeAnnotations(CtElement element) { for (CtAnnotation annotation : element.getAnnotations()) { + + // if element is a type reference and the parent is a typed element + // which contains exactly the same annotation, then we are certainly in this case: + // @myAnnotation String myField + // in which case the annotation is attached to the type and the variable + // in that case, we only print the annotation once. if (element.isParentInitialized() && element instanceof CtTypeReference && (element.getParent() instanceof CtTypedElement) && element.getParent().getAnnotations().contains(annotation)) { continue; } diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java index adcdc6cf891..066db246693 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderQuery.java @@ -198,7 +198,7 @@ static boolean hasAnnotationWithType(Annotation a, CtAnnotatedElementType elemen } } - // true here means that the target annotation is not mandatory and we don't have found it + // true here means that the target annotation is not mandatory and we have not found it return !shouldTargetAnnotationExists && !targetAnnotationExists; } diff --git a/src/main/java/spoon/support/compiler/jdt/ParentExiter.java b/src/main/java/spoon/support/compiler/jdt/ParentExiter.java index d8a3658075f..f9f07c9c1ea 100644 --- a/src/main/java/spoon/support/compiler/jdt/ParentExiter.java +++ b/src/main/java/spoon/support/compiler/jdt/ParentExiter.java @@ -137,7 +137,7 @@ public void scanCtElement(CtElement e) { CtAnnotatedElementType annotatedElementType = CtAnnotation.getAnnotatedElementTypeForCtElement(e); annotatedElementType = (e instanceof CtTypeParameter || e instanceof CtTypeParameterReference) ? CtAnnotatedElementType.TYPE_USE : annotatedElementType; - // in case of noclasspath, we can be 100% sure, so we guess it must be attached... + // in case of noclasspath, we cannot be 100% sure, so we guess it must be attached... if (this.jdtTreeBuilder.getFactory().getEnvironment().getNoClasspath() || (annotatedElementType != null && JDTTreeBuilderQuery.hasAnnotationWithType((Annotation) childJDT, annotatedElementType))) { e.addAnnotation((CtAnnotation) child); }