Skip to content

Commit

Permalink
feat: add metho CtMethod#getTopDefinitions
Browse files Browse the repository at this point in the history
  • Loading branch information
monperrus committed Feb 9, 2018
1 parent b547c30 commit a108db2
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 2 deletions.
9 changes: 9 additions & 0 deletions src/main/java/spoon/reflect/declaration/CtMethod.java
Expand Up @@ -19,6 +19,8 @@
import spoon.reflect.annotations.PropertyGetter;
import spoon.reflect.annotations.PropertySetter;

import java.util.Collection;

import static spoon.reflect.path.CtRole.IS_DEFAULT;


Expand Down Expand Up @@ -50,4 +52,11 @@ public interface CtMethod<T> extends CtExecutable<T>, CtTypeMember, CtFormalType

@Override
CtMethod<T> clone();

/**
* Returns the top-most methods in the hierarchy defining this method
* (in super class and super interfaces).
* Returns the empty collection if defined here for the first time.
*/
Collection<CtMethod<?>> getTopDefinitions();
}
31 changes: 31 additions & 0 deletions src/main/java/spoon/support/reflect/declaration/CtMethodImpl.java
Expand Up @@ -27,11 +27,13 @@
import spoon.reflect.path.CtRole;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.CtVisitor;
import spoon.reflect.visitor.filter.AllTypeMembersFunction;
import spoon.support.reflect.CtExtendedModifier;
import spoon.support.reflect.CtModifierHandler;
import spoon.support.visitor.ClassTypingContext;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

Expand Down Expand Up @@ -217,6 +219,35 @@ public CtMethod<T> clone() {
return (CtMethod<T>) super.clone();
}

@Override
public Collection<CtMethod<?>> getTopDefinitions() {
List<CtMethod<?>> s = new ArrayList<>();

// first collect potential declarations of this method in the type hierarchy
ClassTypingContext context = new ClassTypingContext(this.getDeclaringType());
for (Object m : getDeclaringType().map(new AllTypeMembersFunction(CtMethod.class)).list()) {
if (m != this && context.isOverriding(this, (CtMethod<?>) m)) {
s.add((CtMethod) m);
}
}

// now removing the intermediate methods for which there exists a definition upper in the hierarchy
List<CtMethod<?>> finalMeths = new ArrayList<>(s);
for (CtMethod m1 : s) {
ClassTypingContext context2 = new ClassTypingContext(m1.getDeclaringType());
boolean m1IsIntermediate = false;
for (CtMethod m2 : s) {
if (context2.isOverriding(m1, m2)) {
m1IsIntermediate = true;
}
}
if (!m1IsIntermediate) {
finalMeths.add(m1);
}
}
return finalMeths;
}

@Override
public boolean isPublic() {
return this.modifierHandler.isPublic();
Expand Down
46 changes: 46 additions & 0 deletions src/test/java/spoon/test/filters/FilterTest.java
Expand Up @@ -12,10 +12,13 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.junit.Before;
import org.junit.Test;
Expand Down Expand Up @@ -74,6 +77,7 @@
import spoon.reflect.visitor.filter.ReturnOrThrowFilter;
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.support.comparator.DeepRepresentationComparator;
import spoon.support.comparator.QualifiedNameComparator;
import spoon.support.reflect.declaration.CtMethodImpl;
import spoon.support.visitor.SubInheritanceHierarchyResolver;
import spoon.test.filters.testclasses.AbstractTostada;
Expand Down Expand Up @@ -382,6 +386,48 @@ public void testOverriddenMethodsFromSubClassOfAbstractClass() throws Exception
assertEquals(Tostada.class, overridenMethodsFromSub.get(1).getParent(CtClass.class).getActualClass());
}

@Test
public void testgetTopDefinitions() throws Exception {
// contract: getTopDefinitions returns the correct answer
final Launcher launcher = new Launcher();
launcher.setArgs(new String[] {"--output-type", "nooutput" });
launcher.addInputResource("./src/test/java/spoon/test/filters/testclasses");
launcher.run();

// start with ToStatda
final CtClass<Tostada> aTostada = launcher.getFactory().Class().get(Tostada.class);
List<CtMethod<?>> methods;

methods = orderByName(aTostada.getMethodsByName("make").get(0).getTopDefinitions());
assertEquals(2, methods.size());
assertEquals("AbstractTostada", methods.get(0).getDeclaringType().getSimpleName());
assertEquals("ITostada", methods.get(1).getDeclaringType().getSimpleName());

methods = orderByName(aTostada.getMethodsByName("prepare").get(0).getTopDefinitions());
assertEquals(1, methods.size());
assertEquals("AbstractTostada", methods.get(0).getDeclaringType().getSimpleName());

methods = orderByName(aTostada.getMethodsByName("toString").get(0).getTopDefinitions());
assertEquals(1, methods.size());
assertEquals("Object", methods.get(0).getDeclaringType().getSimpleName());

methods = orderByName(aTostada.getMethodsByName("honey").get(0).getTopDefinitions());
assertEquals(2, methods.size());
assertEquals("AbstractTostada", methods.get(0).getDeclaringType().getSimpleName());
assertEquals("Honey", methods.get(1).getDeclaringType().getSimpleName());
}

private List<CtMethod<?>> orderByName(Collection<CtMethod<?>> meths) {
List<CtMethod<?>> ordered = new ArrayList<>(meths);
ordered.sort(new Comparator<CtMethod<?>>() {
@Override
public int compare(CtMethod<?> o1, CtMethod<?> o2) {
return o1.getParent(CtType.class).getQualifiedName().compareTo(o2.getParent(CtType.class).getQualifiedName());
}
});
return ordered;
}

@Test
public void testOverriddenMethodFromInterface() throws Exception {
// contract: When we declare a method in an interface, we must return an empty list
Expand Down
Expand Up @@ -33,4 +33,8 @@ public ITostada make() {
}

public abstract void prepare();

public void honey() {

}
}
4 changes: 3 additions & 1 deletion src/test/java/spoon/test/filters/testclasses/ITostada.java
Expand Up @@ -16,6 +16,8 @@
*/
package spoon.test.filters.testclasses;

public interface ITostada {
public interface ITostada extends Foo {
ITostada make();
}
interface Foo {
}
17 changes: 16 additions & 1 deletion src/test/java/spoon/test/filters/testclasses/Tostada.java
Expand Up @@ -17,7 +17,8 @@

package spoon.test.filters.testclasses;

public class Tostada extends AbstractTostada {

public class Tostada extends AbstractTostada implements Honey {
@Override
public ITostada make() {
return new Tostada() {
Expand All @@ -32,4 +33,18 @@ public void prepare() {
@Override
public void prepare() {
}

@Override
public String toString() {
return "";
}

@Override
public void honey() {

}
}

interface Honey {
void honey();
}

0 comments on commit a108db2

Please sign in to comment.