Skip to content

Commit

Permalink
feature CtLambda#getMethod() + test
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky committed Feb 5, 2017
1 parent 870a297 commit 2e410bb
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 1 deletion.
8 changes: 8 additions & 0 deletions src/main/java/spoon/reflect/code/CtLambda.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
package spoon.reflect.code;

import spoon.reflect.declaration.CtExecutable;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.reference.CtTypeReference;
import spoon.support.DerivedProperty;
import spoon.support.UnsettableProperty;

import java.util.Set;
Expand Down Expand Up @@ -59,6 +61,12 @@ public interface CtLambda<T> extends CtExpression<T>, CtExecutable<T> {
*/
CtExpression<T> getExpression();

/**
* @return a method which is implemented by this lambda expression
*/
@DerivedProperty
<R> CtMethod<R> getMethod();

/**
* Sets the expression in the body of the lambda. Nothing will change
* if the lambda already has a value in the body attribute.
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/spoon/reflect/factory/ExecutableFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ public <T> CtExecutableReference<T> createReference(CtExecutable<T> e) {
boolean isStatic = ((CtMethod) e).hasModifier(ModifierKind.STATIC);
return createReference(((CtMethod<T>) e).getDeclaringType().getReference(), isStatic, ((CtMethod<T>) e).getType().clone(), executableName, refs);
} else if (e instanceof CtLambda) {
return createReference(e.getParent(CtType.class).getReference(), e.getType(), executableName, refs);
CtMethod<T> lambdaMethod = ((CtLambda) e).getMethod();
return createReference(e.getParent(CtType.class).getReference(), lambdaMethod == null ? null : lambdaMethod.getType(), executableName, refs);
} else if (e instanceof CtAnonymousExecutable) {
return createReference(((CtAnonymousExecutable) e).getDeclaringType().getReference(), e.getType().clone(), executableName);
}
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/spoon/support/reflect/code/CtLambdaImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@
import spoon.reflect.code.CtLambda;
import spoon.reflect.code.CtStatement;
import spoon.reflect.declaration.CtExecutable;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtNamedElement;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.reference.CtExecutableReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.CtVisitor;
Expand Down Expand Up @@ -86,6 +89,41 @@ public <C extends CtBodyHolder> C setBody(CtStatement statement) {
return (C) this;
}

@SuppressWarnings("unchecked")
@Override
public <R> CtMethod<R> getMethod() {
//The type of this lambda expression. For example: `Consumer<Integer>`
CtTypeReference<T> lambdaTypeRef = getType();
if (lambdaTypeRef == null) {
//it can be null in noclasspath mode, so we do not know which method is called, by lambda
return null;
}
CtType<T> lambdaType = lambdaTypeRef.getTypeDeclaration();
if (lambdaType.isInterface() == false) {
throw new SpoonException("The lambda can be based on interface only. But type " + lambdaTypeRef.getQualifiedName() + " is not an interface");
}
Set<CtMethod<?>> lambdaTypeMethods = lambdaType.getAllMethods();
CtMethod<?> lambdaExecutableMethod = null;
if (lambdaTypeMethods.size() == 1) {
//even the default method can be used, if it is the only one
lambdaExecutableMethod = lambdaTypeMethods.iterator().next();
} else {
for (CtMethod<?> method : lambdaTypeMethods) {
if (method.isDefaultMethod() || method.hasModifier(ModifierKind.PRIVATE)) {
continue;
}
if (lambdaExecutableMethod != null) {
throw new SpoonException("The lambda can be based on interface, which has only one method. But " + lambdaTypeRef.getQualifiedName() + " has at least two: " + lambdaExecutableMethod.getSignature() + " and " + method.getSignature());
}
lambdaExecutableMethod = method;
}
}
if (lambdaExecutableMethod == null) {
throw new SpoonException("The lambda can be based on interface, which has one method. But " + lambdaTypeRef.getQualifiedName() + " has no one");
}
return (CtMethod<R>) lambdaExecutableMethod;
}

@Override
public List<CtParameter<?>> getParameters() {
return unmodifiableList(parameters);
Expand Down
20 changes: 20 additions & 0 deletions src/test/java/spoon/test/lambda/LambdaTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import java.io.File;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -341,6 +342,25 @@ public void testBuildExecutableReferenceFromLambda() throws Exception {
assertNotNull(collect);
assertEquals(1, collect.size());
}

@Test
public void testEqualsLambdaParameterRef() throws Exception {
CtLambda<?> lambda = getLambdaInFooByNumber(8);
CtParameter<?> param = (CtParameter<?>)lambda.getParameters().get(0);
CtParameterReference paramRef1 = param.getReference();
CtParameterReference paramRef2 = lambda.filterChildren(new TypeFilter<>(CtParameterReference.class)).first();
assertTrue(paramRef1.getDeclaringExecutable().getType().equals(paramRef2.getDeclaringExecutable().getType()));
assertTrue(paramRef1.equals(paramRef2));
}

@Test
public void testLambdaMethod() throws Exception {
CtLambda<?> lambda = getLambdaInFooByNumber(8);
CtMethod<?> method = lambda.getMethod();
CtTypeReference<?> iface = lambda.getType();
assertEquals(Consumer.class.getName(), iface.getQualifiedName());
assertEquals(iface.getTypeDeclaration().getMethodsByName("accept").get(0), method);
}

private void assertTypedBy(Class<?> expectedType, CtTypeReference<?> type) {
assertEquals("Lambda must be typed", expectedType, type.getActualClass());
Expand Down
7 changes: 7 additions & 0 deletions src/test/java/spoon/test/lambda/testclasses/Foo.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;

public class Foo {
Expand Down Expand Up @@ -48,6 +49,12 @@ public void m8() {
}
}

public void m9() {
Consumer<Integer> c = (field)->{
field=1;
};
}

public static void printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester) {
for (Person p : roster) {
if (tester.test(p)) {
Expand Down

0 comments on commit 2e410bb

Please sign in to comment.