diff --git a/chore/travis/travis-verify.sh b/chore/travis/travis-verify.sh index 1ed43defdb5..45ee2fb8980 100755 --- a/chore/travis/travis-verify.sh +++ b/chore/travis/travis-verify.sh @@ -7,4 +7,4 @@ source /opt/jdk_switcher/jdk_switcher.sh pip install --user CommonMark requests pygithub -jdk_switcher use oraclejdk9 && mvn -Djava.src.version=1.9 verify site install -DskipTests && python ./chore/check-links-in-doc.py \ No newline at end of file +jdk_switcher use oraclejdk9 && mvn -Djava.src.version=1.9 verify site javadoc:jar install -DskipTests && python ./chore/check-links-in-doc.py \ No newline at end of file diff --git a/pom.xml b/pom.xml index c53bcdea913..bf1b25829dc 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ fr.inria.gforge.spoon spoon-core jar - 6.0.0-SNAPSHOT + 7.0.0-SNAPSHOT Spoon Core Spoon is a tool for meta-programming, analysis and transformation of Java programs. http://spoon.gforge.inria.fr/ @@ -240,7 +240,11 @@ org.apache.commons commons-lang3 3.5 - test + + + com.fasterxml.jackson.core + jackson-databind + 2.9.2 @@ -508,6 +512,11 @@ + + org.apache.maven.plugins + maven-javadoc-plugin + 3.0.0-M1 + diff --git a/src/main/java/spoon/processing/ProcessorPropertiesImpl.java b/src/main/java/spoon/processing/ProcessorPropertiesImpl.java index 4cec552a9c5..2d470f4b8fa 100644 --- a/src/main/java/spoon/processing/ProcessorPropertiesImpl.java +++ b/src/main/java/spoon/processing/ProcessorPropertiesImpl.java @@ -16,15 +16,24 @@ */ package spoon.processing; +import org.apache.commons.lang3.ClassUtils; + import java.util.HashMap; import java.util.Map; public class ProcessorPropertiesImpl implements ProcessorProperties { - private final Map _properties = new HashMap<>(); public T get(Class type, String name) { - return (T) _properties.get(name); + if (type.isPrimitive()) { + type = (Class) ClassUtils.primitiveToWrapper(type); + } + T result = (T) _properties.get(name); + if (result == null) { + return null; + } else { + return (type.isAssignableFrom(result.getClass())) ? result : null; + } } public void set(String name, Object o) { diff --git a/src/main/java/spoon/reflect/declaration/CtElement.java b/src/main/java/spoon/reflect/declaration/CtElement.java index 09d363a3885..e0186696e37 100644 --- a/src/main/java/spoon/reflect/declaration/CtElement.java +++ b/src/main/java/spoon/reflect/declaration/CtElement.java @@ -317,7 +317,7 @@ List getAnnotatedChildren( /** * @return a a single value (eg a CtElement), List, Set or Map depending on this `element` and `role`. Returned collections are read-only. - * @param the role of the returned attribute with respect to this element. + * @param role the role of the returned attribute with respect to this element. * * For instance, "klass.getValueByRole(CtRole.METHOD)" returns a list of methods. * @@ -327,7 +327,7 @@ List getAnnotatedChildren( /** * Sets a field according to a role. - * @param the role of the field to be set + * @param role the role of the field to be set * @param value to be assigned to this field. */ E setValueByRole(CtRole role, T value); diff --git a/src/main/java/spoon/reflect/factory/Factory.java b/src/main/java/spoon/reflect/factory/Factory.java index 7ad90411188..137094e683c 100644 --- a/src/main/java/spoon/reflect/factory/Factory.java +++ b/src/main/java/spoon/reflect/factory/Factory.java @@ -915,7 +915,7 @@ public interface Factory { CtPackage createPackage(CtPackage parent, String simpleName); /** - * @see CoreFactory#create(Class) + * @see CoreFactory#create(Class) */ CtElement createElement(Class klass); diff --git a/src/main/java/spoon/reflect/visitor/CtBiScannerDefault.java b/src/main/java/spoon/reflect/visitor/CtBiScannerDefault.java index a79c74488d4..23059d1659a 100644 --- a/src/main/java/spoon/reflect/visitor/CtBiScannerDefault.java +++ b/src/main/java/spoon/reflect/visitor/CtBiScannerDefault.java @@ -23,7 +23,7 @@ * Ensures that all children nodes are visited once, a visit means three method * calls, one call to "enter", one call to "exit" and one call to biScan. * - * This class is generated automatically by the processor {@link spoon.generating.CtBiScannerGenerator}. + * This class is generated automatically by the processor spoon.generating.CtBiScannerGenerator. * * Is used by EqualsVisitor. */ diff --git a/src/main/java/spoon/support/reflect/package.html b/src/main/java/spoon/support/reflect/package.html index 4cbaa2cb948..2a5dfaed307 100644 --- a/src/main/java/spoon/support/reflect/package.html +++ b/src/main/java/spoon/support/reflect/package.html @@ -24,7 +24,7 @@

This package provides an implementation of the spoon.reflect package.

-

The classes of this package should not be instantiated directly, but by using {@link spoon.reflect.CtFactory}. +

The classes of this package should not be instantiated directly, but by using {@link spoon.reflect.factory.Factory}.

Related Documentation

diff --git a/src/main/java/spoon/support/visitor/clone/CloneBuilder.java b/src/main/java/spoon/support/visitor/clone/CloneBuilder.java index d94e459ba21..2a01a4d2bab 100644 --- a/src/main/java/spoon/support/visitor/clone/CloneBuilder.java +++ b/src/main/java/spoon/support/visitor/clone/CloneBuilder.java @@ -20,7 +20,7 @@ /** * Used to set all data in the cloned element. * - * This class is generated automatically by the processor {@link spoon.generating.CloneVisitorGenerator}. + * This class is generated automatically by the processor spoon.generating.CloneVisitorGenerator. */ public class CloneBuilder extends spoon.reflect.visitor.CtInheritanceScanner { private spoon.reflect.declaration.CtElement other; diff --git a/src/main/java/spoon/support/visitor/clone/CloneVisitor.java b/src/main/java/spoon/support/visitor/clone/CloneVisitor.java index 9ccd1df166a..1a4dcdd896b 100644 --- a/src/main/java/spoon/support/visitor/clone/CloneVisitor.java +++ b/src/main/java/spoon/support/visitor/clone/CloneVisitor.java @@ -20,7 +20,7 @@ /** * Used to clone a given element. * - * This class is generated automatically by the processor {@link spoon.generating.CloneVisitorGenerator}. + * This class is generated automatically by the processor spoon.generating.CloneVisitorGenerator. */ public class CloneVisitor extends spoon.reflect.visitor.CtScanner { private final spoon.support.visitor.equals.CloneHelper cloneHelper; diff --git a/src/main/java/spoon/support/visitor/replace/ReplacementVisitor.java b/src/main/java/spoon/support/visitor/replace/ReplacementVisitor.java index d0995375393..efab54a4ae2 100644 --- a/src/main/java/spoon/support/visitor/replace/ReplacementVisitor.java +++ b/src/main/java/spoon/support/visitor/replace/ReplacementVisitor.java @@ -20,7 +20,7 @@ /** * Used to replace an element by another one. * - * This class is generated automatically by the processor {@link spoon.generating.ReplacementVisitorGenerator}. + * This class is generated automatically by the processor spoon.generating.ReplacementVisitorGenerator. */ public class ReplacementVisitor extends spoon.reflect.visitor.CtScanner { private spoon.reflect.declaration.CtElement original; diff --git a/src/main/java/spoon/testing/utils/ProcessorUtils.java b/src/main/java/spoon/testing/utils/ProcessorUtils.java index 07bfe6bb95f..89a8a186e73 100644 --- a/src/main/java/spoon/testing/utils/ProcessorUtils.java +++ b/src/main/java/spoon/testing/utils/ProcessorUtils.java @@ -16,6 +16,7 @@ */ package spoon.testing.utils; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.log4j.Level; import spoon.Launcher; import spoon.SpoonException; @@ -30,6 +31,8 @@ import java.util.Collection; public final class ProcessorUtils { + private static final ObjectMapper converter = new ObjectMapper(); + private ProcessorUtils() { throw new AssertionError(); } @@ -52,9 +55,19 @@ public static void initProperties(Processor p, ProcessorProperties properties throw new SpoonException(e); } } else { - p.getFactory().getEnvironment().report(p, Level.WARN, - "No value found for property '" + f.getName() + "' in processor " + p.getClass() - .getName()); + obj = properties.get(String.class, f.getName()); + if (obj != null) { + try { + obj = converter.readValue((String) obj, f.getType()); + f.setAccessible(true); + f.set(p, obj); + } catch (Exception e) { + throw new SpoonException("Error while assigning the value to " + f.getName(), e); + } + } else { + p.getFactory().getEnvironment().report(p, Level.WARN, + "No value found for property '" + f.getName() + "' in processor " + p.getClass().getName()); + } } } } diff --git a/src/test/java/spoon/MavenLauncherTest.java b/src/test/java/spoon/MavenLauncherTest.java index 5c0009120fb..3a83265a8e3 100644 --- a/src/test/java/spoon/MavenLauncherTest.java +++ b/src/test/java/spoon/MavenLauncherTest.java @@ -11,7 +11,7 @@ public void spoonMavenLauncherTest() { // without the tests MavenLauncher launcher = new MavenLauncher("./", MavenLauncher.SOURCE_TYPE.APP_SOURCE); - assertEquals(5, launcher.getEnvironment().getSourceClasspath().length); + assertEquals(7, launcher.getEnvironment().getSourceClasspath().length); // 52 because of the sub folders of src/main/java assertEquals(52, launcher.getModelBuilder().getInputSources().size()); diff --git a/src/test/java/spoon/generating/CloneVisitorGenerator.java b/src/test/java/spoon/generating/CloneVisitorGenerator.java index 4f99f99bda4..b837f98f22c 100644 --- a/src/test/java/spoon/generating/CloneVisitorGenerator.java +++ b/src/test/java/spoon/generating/CloneVisitorGenerator.java @@ -101,7 +101,11 @@ public void visitCtMethod(CtMethod element) { // Changes body of the cloned method. for (int i = 1; i < clone.getBody().getStatements().size() - 1; i++) { - final CtInvocation targetInvocation = (CtInvocation) ((CtInvocation) clone.getBody().getStatement(i)).getArguments().get(1); + List invArgs = ((CtInvocation) clone.getBody().getStatement(i)).getArguments(); + if (invArgs.size() <= 1) { + throw new RuntimeException("You forget the role argument in line "+i+" of method "+element.getSimpleName()+" from "+element.getDeclaringType().getQualifiedName()); + } + final CtInvocation targetInvocation = (CtInvocation) invArgs.get(1); if ("getValue".equals(targetInvocation.getExecutable().getSimpleName()) && "CtLiteral".equals(targetInvocation.getExecutable().getDeclaringType().getSimpleName())) { clone.getBody().getStatement(i--).delete(); continue; diff --git a/src/test/java/spoon/generating/CtBiScannerGenerator.java b/src/test/java/spoon/generating/CtBiScannerGenerator.java index c8f4b741d48..2b42fbf6b46 100644 --- a/src/test/java/spoon/generating/CtBiScannerGenerator.java +++ b/src/test/java/spoon/generating/CtBiScannerGenerator.java @@ -69,7 +69,11 @@ public void process() { clone.getBody().insertBegin(peek); for (int i = 2; i < clone.getBody().getStatements().size() - 1; i++) { - final CtInvocation targetInvocation = (CtInvocation) ((CtInvocation) clone.getBody().getStatement(i)).getArguments().get(1); + List invArgs = ((CtInvocation) clone.getBody().getStatement(i)).getArguments(); + if (invArgs.size() <= 1) { + throw new RuntimeException("You forget the role argument in line "+i+" of method "+element.getSimpleName()+" from "+element.getDeclaringType().getQualifiedName()); + } + final CtInvocation targetInvocation = (CtInvocation) invArgs.get(1); if ("getValue".equals(targetInvocation.getExecutable().getSimpleName()) && "CtLiteral".equals(targetInvocation.getExecutable().getDeclaringType().getSimpleName())) { clone.getBody().getStatement(i--).delete(); continue; diff --git a/src/test/java/spoon/generating/clone/CloneVisitorTemplate.java b/src/test/java/spoon/generating/clone/CloneVisitorTemplate.java index afd8dba8a63..5649670e10a 100644 --- a/src/test/java/spoon/generating/clone/CloneVisitorTemplate.java +++ b/src/test/java/spoon/generating/clone/CloneVisitorTemplate.java @@ -24,7 +24,7 @@ /** * Used to clone a given element. * - * This class is generated automatically by the processor {@link spoon.generating.CloneVisitorGenerator}. + * This class is generated automatically by the processor spoon.generating.CloneVisitorGenerator. */ class CloneVisitorTemplate extends CtScanner { private final CloneHelper cloneHelper; diff --git a/src/test/java/spoon/generating/replace/ReplaceScanner.java b/src/test/java/spoon/generating/replace/ReplaceScanner.java index 612612de258..f01f2f889fa 100644 --- a/src/test/java/spoon/generating/replace/ReplaceScanner.java +++ b/src/test/java/spoon/generating/replace/ReplaceScanner.java @@ -83,6 +83,9 @@ public void visitCtMethod(CtMethod element) { for (int i = 1; i < element.getBody().getStatements().size() - 1; i++) { CtInvocation inv = element.getBody().getStatement(i); List> invArgs = new ArrayList<>(inv.getArguments()); + if (invArgs.size() <= 1) { + throw new RuntimeException("You forget the role argument in line "+i+" of method "+element.getSimpleName()+" from "+element.getDeclaringType().getQualifiedName()); + } //remove role argument invArgs.remove(0); CtInvocation getter = (CtInvocation) invArgs.get(0); diff --git a/src/test/java/spoon/generating/replace/ReplacementVisitor.java b/src/test/java/spoon/generating/replace/ReplacementVisitor.java index 5141b4aacde..75b97bf77f2 100644 --- a/src/test/java/spoon/generating/replace/ReplacementVisitor.java +++ b/src/test/java/spoon/generating/replace/ReplacementVisitor.java @@ -36,7 +36,7 @@ /** * Used to replace an element by another one. * - * This class is generated automatically by the processor {@link spoon.generating.ReplacementVisitorGenerator}. + * This class is generated automatically by the processor spoon.generating.ReplacementVisitorGenerator. */ class ReplacementVisitor extends CtScanner { public static void replace(CtElement original, CtElement replace) { diff --git a/src/test/java/spoon/generating/scanner/CtBiScannerTemplate.java b/src/test/java/spoon/generating/scanner/CtBiScannerTemplate.java index 077dd027753..96db821ac5d 100644 --- a/src/test/java/spoon/generating/scanner/CtBiScannerTemplate.java +++ b/src/test/java/spoon/generating/scanner/CtBiScannerTemplate.java @@ -24,7 +24,7 @@ * Ensures that all children nodes are visited once, a visit means three method * calls, one call to "enter", one call to "exit" and one call to biScan. * - * This class is generated automatically by the processor {@link spoon.generating.CtBiScannerGenerator}. + * This class is generated automatically by the processor spoon.generating.CtBiScannerGenerator. * * Is used by EqualsVisitor. */ diff --git a/src/test/java/spoon/reflect/visitor/CtScannerTest.java b/src/test/java/spoon/reflect/visitor/CtScannerTest.java index 93383d8c165..2b5902cb5c7 100644 --- a/src/test/java/spoon/reflect/visitor/CtScannerTest.java +++ b/src/test/java/spoon/reflect/visitor/CtScannerTest.java @@ -181,14 +181,16 @@ public boolean matches(CtInvocation element) { } else { c.nbChecks++; //System.out.println(invocation.toString()); + + // contract: the scan method is called with the same role as the one set on field / property + CtRole expectedRole = metaModel.getRoleOfMethod((CtMethod)invocation.getExecutable().getDeclaration()); + CtInvocation scanInvocation = invocation.getParent(CtInvocation.class); + String realRoleName = ((CtFieldRead) scanInvocation.getArguments().get(0)).getVariable().getSimpleName(); + if(expectedRole.name().equals(realRoleName) == false) { + problems.add("Wrong role " + realRoleName + " used in " + scanInvocation.getPosition()); + } } - // contract: the scan method is called with the same role as the one set on field / property - CtRole expectedRole = metaModel.getRoleOfMethod((CtMethod) invocation.getExecutable().getDeclaration()); - CtInvocation scanInvocation = invocation.getParent(CtInvocation.class); - String realRoleName = ((CtFieldRead) scanInvocation.getArguments().get(0)).getVariable().getSimpleName(); - if (expectedRole.name().equals(realRoleName) == false) { - problems.add("Wrong role " + realRoleName + " used in " + scanInvocation.getPosition()); - } + }); calledMethods.removeAll(checkedMethods); diff --git a/src/test/java/spoon/test/processing/ProcessingTest.java b/src/test/java/spoon/test/processing/ProcessingTest.java index c4f2b78ef6a..bbd89dfb488 100644 --- a/src/test/java/spoon/test/processing/ProcessingTest.java +++ b/src/test/java/spoon/test/processing/ProcessingTest.java @@ -23,10 +23,17 @@ import spoon.test.processing.testclasses.CtTypeProcessor; import spoon.testing.utils.ProcessorUtils; +import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -165,6 +172,18 @@ class AProcessor extends AbstractManualProcessor { @Property Object anObject; + @Property + int[] arrayInt; + + @Property + List listString; + + @Property + boolean[] arrayBoolean; + + @Property + Map mapStringDouble; + @Override public void process() { @@ -172,6 +191,8 @@ public void process() { }; AProcessor p = new AProcessor(); + Launcher launcher = new Launcher(); + p.setFactory(launcher.getFactory()); ProcessorProperties props = new ProcessorPropertiesImpl(); props.set("aString", "foo"); @@ -179,11 +200,123 @@ public void process() { Object o = new Object(); props.set("anObject", o); + int[] arrayInt = new int[]{ 1, 2, 3}; + props.set("arrayInt", arrayInt); + props.set("listString", Arrays.asList(new String[]{"42"})); + + boolean[] arrayBoolean = new boolean[] {true}; + props.set("arrayBoolean", arrayBoolean); + HashMap mapTest = new HashMap<>(); + mapTest.put("foobar",42.42); + props.set("mapStringDouble", mapTest); + ProcessorUtils.initProperties(p, props); assertEquals("foo", p.aString); assertEquals(5, p.anInt); assertSame(o, p.anObject); + assertSame(arrayInt, p.arrayInt); + assertEquals(Arrays.asList(new String[]{"42"}), p.listString); + assertSame(arrayBoolean, p.arrayBoolean); + assertSame(mapTest, p.mapStringDouble); + } + + @Test + public void testInitPropertiesWithWrongType() throws Exception { + class AProcessor extends AbstractManualProcessor { + @Property + String aString; + + @Property + int anInt; + + @Property + Object anObject; + + @Override + public void process() { + + } + }; + + AProcessor p = new AProcessor(); + Launcher launcher = new Launcher(); + p.setFactory(launcher.getFactory()); + + ProcessorProperties props = new ProcessorPropertiesImpl(); + props.set("aString", "foo"); + props.set("anObject", "foo"); + props.set("anInt", "foo"); + + try { + ProcessorUtils.initProperties(p, props); + fail(); + } catch (SpoonException e) { + assertTrue(e.getMessage().contains("anInt")); + } + + + assertEquals("foo", p.aString); + assertEquals(0, p.anInt); + assertNull(p.anObject); + } + + @Test + public void testInitPropertiesWithStringType() throws Exception { + class AProcessor extends AbstractManualProcessor { + @Property + String aString; + + @Property + int anInt; + + @Property + Object anObject; + + @Property + int[] arrayInt; + + @Property + List listString; + + @Property + boolean[] arrayBoolean; + + @Property + Map mapStringDouble; + + @Override + public void process() { + + } + }; + + AProcessor p = new AProcessor(); + Launcher launcher = new Launcher(); + p.setFactory(launcher.getFactory()); + + ProcessorProperties props = new ProcessorPropertiesImpl(); + props.set("aString", "foo"); + props.set("anInt", "42"); + props.set("anObject", "{}"); + props.set("arrayInt", "[42,43]"); + props.set("listString", "[\"foo\", \"bar\"]"); + props.set("arrayBoolean", "[true]"); + props.set("mapStringDouble","{\"foo\": 10.21, \"bar\": 14.42}"); + + ProcessorUtils.initProperties(p, props); + + assertEquals("foo", p.aString); + assertEquals(42, p.anInt); + assertNotNull(p.anObject); + + assertArrayEquals(new int[] {42, 43}, p.arrayInt); + assertEquals(Arrays.asList(new String[]{"foo", "bar"}), p.listString); + assertArrayEquals(new boolean[]{true}, p.arrayBoolean); + Map mamap = new HashMap<>(); + mamap.put("foo", 10.21); + mamap.put("bar", 14.42); + assertEquals(mamap, p.mapStringDouble); } @Test