Skip to content

Commit

Permalink
fix(enum): handle synthetic methods of enums (fixes #793)
Browse files Browse the repository at this point in the history
  • Loading branch information
fedorov-s-n authored and monperrus committed Aug 19, 2016
1 parent d6dbd50 commit 9266242
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 3 deletions.
74 changes: 72 additions & 2 deletions src/main/java/spoon/support/reflect/declaration/CtEnumImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,31 @@

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<T extends Enum<?>> extends CtClassImpl<T> implements CtEnum<T> {
private static final long serialVersionUID = 1L;

private List<CtEnumValue<?>> enumValues = CtElementImpl.<CtEnumValue<?>>emptyList();

private CtMethod<T[]> valuesMethod;

private CtMethod<T> valueOfMethod;

@Override
public void accept(CtVisitor visitor) {
visitor.visitCtEnum(this);
}

@Override
public Set<CtMethod<?>> getAllMethods() {
return getMethods();
Set<CtMethod<?>> allMethods = new HashSet<>(getMethods());
allMethods.add(valuesMethod());
allMethods.add(valueOfMethod());
return allMethods;
}

@Override
Expand All @@ -50,7 +59,7 @@ public boolean isSubtypeOf(CtTypeReference<?> type) {
return true;
}
}
return false;
return getSuperclass().isSubtypeOf(type);
}

@Override
Expand Down Expand Up @@ -111,4 +120,65 @@ public CtField<?> getField(String name) {
public CtEnum<T> clone() {
return (CtEnum<T>) 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 <R> CtMethod<R> 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 <R> CtMethod<R> getMethod(CtTypeReference<R> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void testScannerEnum() throws Exception {
final CtEnum<TextStyle> 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);
Expand Down
11 changes: 11 additions & 0 deletions src/test/java/spoon/test/reference/Enum.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package spoon.test.reference;

public enum Enum {
A,
B,
C;

public static Enum getFirst(){
return valueOf("A");
}
}
18 changes: 18 additions & 0 deletions src/test/java/spoon/test/reference/ExecutableReferenceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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>(CtInvocation.class) {
@Override
public boolean matches(CtInvocation element) {
return super.matches(element)
&& element.getExecutable().getSimpleName().equals("valueOf");
}
}).get(0);
assertNotNull(invocation.getExecutable().getExecutableDeclaration());
}
}
11 changes: 11 additions & 0 deletions src/test/java/spoon/test/reference/TypeReferenceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -522,4 +523,14 @@ class B {
class Tacos<K extends A> {
}
}

@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());
}
}

0 comments on commit 9266242

Please sign in to comment.