From 92662420b0231e37d5ab912e2f7b00f6f7849e42 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 19 Aug 2016 12:26:28 +0300 Subject: [PATCH] fix(enum): handle synthetic methods of enums (fixes #793) --- .../reflect/declaration/CtEnumImpl.java | 74 ++++++++++++++++++- .../java/JavaReflectionTreeBuilderTest.java | 2 +- src/test/java/spoon/test/reference/Enum.java | 11 +++ .../reference/ExecutableReferenceTest.java | 18 +++++ .../test/reference/TypeReferenceTest.java | 11 +++ 5 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 src/test/java/spoon/test/reference/Enum.java diff --git a/src/main/java/spoon/support/reflect/declaration/CtEnumImpl.java b/src/main/java/spoon/support/reflect/declaration/CtEnumImpl.java index 4f24184f6ee..0c8b15e4334 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtEnumImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtEnumImpl.java @@ -25,14 +25,20 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; +import spoon.reflect.declaration.ModifierKind; public class CtEnumImpl> extends CtClassImpl implements CtEnum { private static final long serialVersionUID = 1L; private List> enumValues = CtElementImpl.>emptyList(); + private CtMethod valuesMethod; + + private CtMethod valueOfMethod; + @Override public void accept(CtVisitor visitor) { visitor.visitCtEnum(this); @@ -40,7 +46,10 @@ public void accept(CtVisitor visitor) { @Override public Set> getAllMethods() { - return getMethods(); + Set> allMethods = new HashSet<>(getMethods()); + allMethods.add(valuesMethod()); + allMethods.add(valueOfMethod()); + return allMethods; } @Override @@ -50,7 +59,7 @@ public boolean isSubtypeOf(CtTypeReference type) { return true; } } - return false; + return getSuperclass().isSubtypeOf(type); } @Override @@ -111,4 +120,65 @@ public CtField getField(String name) { public CtEnum clone() { return (CtEnum) super.clone(); } + + @Override + public CtTypeReference getSuperclass() { + return getFactory().Type().createReference(Enum.class); + } + + private CtMethod valuesMethod() { + if (valuesMethod == null) { + valuesMethod = getFactory().Core().createMethod(); + valuesMethod.setParent(this); + valuesMethod.addModifier(ModifierKind.PUBLIC); + valuesMethod.addModifier(ModifierKind.STATIC); + valuesMethod.setSimpleName("values"); + valuesMethod.setImplicit(true); + valuesMethod.setType(factory.Type().createArrayReference(getReference())); + } + return valuesMethod; + } + + private CtMethod valueOfMethod() { + if (valueOfMethod == null) { + valueOfMethod = getFactory().Core().createMethod(); + valueOfMethod.setParent(this); + valueOfMethod.addModifier(ModifierKind.PUBLIC); + valueOfMethod.addModifier(ModifierKind.STATIC); + valueOfMethod.setSimpleName("valueOf"); + valueOfMethod.setImplicit(true); + valueOfMethod.addThrownType( + getFactory().Type().createReference(IllegalArgumentException.class)); + valueOfMethod.setType(getReference()); + factory.Method().createParameter(valuesMethod, factory.Type().STRING, "name"); + } + return valueOfMethod; + } + + @Override + public CtMethod getMethod(String name, CtTypeReference... parameterTypes) { + if ("values".equals(name) && parameterTypes.length == 0) { + return valuesMethod(); + } else if ("valueOf".equals(name) && parameterTypes.length == 1 && parameterTypes[0].equals(factory.Type().STRING)) { + return valueOfMethod(); + } else { + return super.getMethod(name, parameterTypes); + } + } + + @Override + public CtMethod getMethod(CtTypeReference returnType, String name, CtTypeReference... parameterTypes) { + if ("values".equals(name) + && parameterTypes.length == 0 + && returnType.equals(getReference())) { + return valuesMethod(); + } else if ("valueOf".equals(name) + && parameterTypes.length == 1 + && parameterTypes[0].equals(factory.Type().STRING) + && returnType.equals(factory.Type().createArrayReference(getReference()))) { + return valueOfMethod(); + } else { + return super.getMethod(returnType, name, parameterTypes); + } + } } diff --git a/src/test/java/spoon/support/visitor/java/JavaReflectionTreeBuilderTest.java b/src/test/java/spoon/support/visitor/java/JavaReflectionTreeBuilderTest.java index bf3277f8db3..fe289269dcd 100644 --- a/src/test/java/spoon/support/visitor/java/JavaReflectionTreeBuilderTest.java +++ b/src/test/java/spoon/support/visitor/java/JavaReflectionTreeBuilderTest.java @@ -44,7 +44,7 @@ public void testScannerEnum() throws Exception { final CtEnum anEnum = new JavaReflectionTreeBuilder(createFactory()).scan(TextStyle.class); assertNotNull(anEnum); assertEquals("java.time.format.TextStyle", anEnum.getQualifiedName()); - assertNull(anEnum.getSuperclass()); + assertNotNull(anEnum.getSuperclass()); assertTrue(anEnum.getFields().size() > 0); assertTrue(anEnum.getEnumValues().size() > 0); assertTrue(anEnum.getMethods().size() > 0); diff --git a/src/test/java/spoon/test/reference/Enum.java b/src/test/java/spoon/test/reference/Enum.java new file mode 100644 index 00000000000..3a775ae317f --- /dev/null +++ b/src/test/java/spoon/test/reference/Enum.java @@ -0,0 +1,11 @@ +package spoon.test.reference; + +public enum Enum { + A, + B, + C; + + public static Enum getFirst(){ + return valueOf("A"); + } +} diff --git a/src/test/java/spoon/test/reference/ExecutableReferenceTest.java b/src/test/java/spoon/test/reference/ExecutableReferenceTest.java index eba2274adef..0f3146e65b6 100644 --- a/src/test/java/spoon/test/reference/ExecutableReferenceTest.java +++ b/src/test/java/spoon/test/reference/ExecutableReferenceTest.java @@ -147,4 +147,22 @@ public boolean matches(final CtExecutable exec) { } }); } + + @Test + public void testInvokeEnumMethod() { + final spoon.Launcher launcher = new spoon.Launcher(); + launcher.addInputResource("./src/test/java/spoon/test/reference/Enum.java"); + launcher.getEnvironment().setNoClasspath(true); + launcher.getEnvironment().setComplianceLevel(8); + launcher.buildModel(); + + CtInvocation invocation = launcher.getModel().getElements(new TypeFilter(CtInvocation.class) { + @Override + public boolean matches(CtInvocation element) { + return super.matches(element) + && element.getExecutable().getSimpleName().equals("valueOf"); + } + }).get(0); + assertNotNull(invocation.getExecutable().getExecutableDeclaration()); + } } diff --git a/src/test/java/spoon/test/reference/TypeReferenceTest.java b/src/test/java/spoon/test/reference/TypeReferenceTest.java index f7c6998691c..2edb73ba7ce 100644 --- a/src/test/java/spoon/test/reference/TypeReferenceTest.java +++ b/src/test/java/spoon/test/reference/TypeReferenceTest.java @@ -44,6 +44,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import spoon.reflect.declaration.CtEnum; import static spoon.testing.utils.ModelUtils.buildClass; import static spoon.testing.utils.ModelUtils.canBeBuilt; import static spoon.testing.utils.ModelUtils.createFactory; @@ -522,4 +523,14 @@ class B { class Tacos { } } + + @Test + public void testCorrectEnumParent() { + final Launcher launcher = new Launcher(); + launcher.getEnvironment().setNoClasspath(true); + launcher.buildModel(); + CtEnum e = launcher.getFactory().Enum().create("spoon.test.reference.EnumE"); + CtTypeReference correctParent = launcher.getFactory().Type().createReference(java.lang.Enum.class); + assertEquals(correctParent, e.getReference().getSuperclass()); + } }