diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaMethod.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaMethod.java index 926f39e7a..49583d184 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaMethod.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaMethod.java @@ -16,9 +16,7 @@ package com.tngtech.archunit.core.domain; import java.lang.reflect.Method; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.function.Function; import java.util.function.Supplier; @@ -125,6 +123,46 @@ public String getDescription() { return "Method <" + getFullName() + ">"; } + @PublicAPI(usage = ACCESS) public boolean isOverridden() { + JavaClass superClass; + for (Optional superClassOpt = + getOwner().getSuperclass(); superClassOpt.isPresent(); + superClassOpt = superClass.getSuperclass()) { + JavaParameterizedType superClassType = (JavaParameterizedType) superClassOpt.get(); + superClass = superClassType.toErasure(); + List> superClassTypeParameters = + superClass.getTypeParameters(); + Map*/, JavaType> typeParametersToOverridenTypes = + new HashMap<>(); + for (int i = 0; i < superClassTypeParameters.size(); i++) { + typeParametersToOverridenTypes.put(superClassTypeParameters.get(i), + superClassType.getActualTypeArguments().get(i)); + } + + for (JavaMethod superMethod : superClass.getAllMethods()) { + if (!superMethod.getName().equals(getName())) { + return false; + } + + List parameterTypes = getParameters(); + List superMethodParameterTypes = superMethod.getParameters(); + if (parameterTypes.size() != superMethodParameterTypes.size()) { + return false; + } + for (int i = 0; i < parameterTypes.size(); i++) { + JavaParameter parameter = parameterTypes.get(i); + JavaParameter superParameter = superMethodParameterTypes.get(i); + if (!parameter.equals(superParameter) && !typeParametersToOverridenTypes.get( + superParameter.getType()).equals(parameter.getType())) { + return false; + } + } + return true; + } + } + return false; + } + @ResolvesTypesViaReflection @MayResolveTypesViaReflection(reason = "Just part of a bigger resolution process") private class ReflectMethodSupplier implements Supplier { diff --git a/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaMethodTest.java b/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaMethodTest.java new file mode 100644 index 000000000..5bc8e6ad8 --- /dev/null +++ b/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaMethodTest.java @@ -0,0 +1,75 @@ +package com.tngtech.archunit.core.domain; + + +import com.tngtech.archunit.core.importer.ClassFileImporter; +import org.junit.Test; + +import java.util.stream.Collectors; + +import static com.tngtech.archunit.testutil.Assertions.assertThat; + +public class JavaMethodTest { + @Test + public void isOverriddenTest() { + class Base { + void method1() { + } + + void method1(int x) { + } + } + class Child extends Base { + void method1() { + } + + void method2() { + } + } + class GrandChild extends Child { + void method1() { + + } + + void method1(int x) { + + } + + void method2() { + + } + + void method3() { + + } + + } + ClassFileImporter importer = new ClassFileImporter(); + JavaClass baseClass = importer.importClass(Base.class); + JavaClass childClass = importer.importClass(Child.class); + JavaClass grandChildClass = importer.importClass(GrandChild.class); + assertThat(baseClass.getMethod("method1").isOverridden()).isFalse(); + assertThat(baseClass.getMethod("method1", int.class).isOverridden()).isFalse(); + assertThat(childClass.getMethod("method1").isOverridden()).isTrue(); + assertThat(childClass.getMethod("method2").isOverridden()).isFalse(); + assertThat(grandChildClass.getMethod("method1").isOverridden()).isTrue(); + assertThat(grandChildClass.getMethod("method1", int.class).isOverridden()).isTrue(); + assertThat(grandChildClass.getMethod("method2").isOverridden()).isTrue(); + assertThat(grandChildClass.getMethod("method3").isOverridden()).isFalse(); + //TODO add testing for methods with generic parameters + } + @Test + public void overridden_generic_methods_are_supported() { + class Parent { + void method(T t) { } + } + class Child extends Parent { + @Override + void method(Integer t) { } + + } + ClassFileImporter classFileImporter = new ClassFileImporter(); + JavaClass childClass = classFileImporter.importClass(Child.class); + JavaMethod method = childClass.getMethod("method", Integer.class); + assertThat(method.isOverridden()).isTrue(); + } +}