Skip to content

Commit 20ec269

Browse files
rweisledercodecholeric
authored andcommitted
add possibility to check for overriding methods to domain and lang
Resolves: #1198 Signed-off-by: Roland Weisleder <roland.weisleder@googlemail.com>
1 parent 8e9744b commit 20ec269

File tree

8 files changed

+212
-2
lines changed

8 files changed

+212
-2
lines changed

archunit/src/main/java/com/tngtech/archunit/core/domain/JavaMethod.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,29 @@
2121
import java.util.Set;
2222
import java.util.function.Function;
2323
import java.util.function.Supplier;
24+
import java.util.stream.Stream;
2425

2526
import com.tngtech.archunit.PublicAPI;
2627
import com.tngtech.archunit.base.ArchUnitException.InconsistentClassPathException;
28+
import com.tngtech.archunit.base.DescribedPredicate;
2729
import com.tngtech.archunit.base.MayResolveTypesViaReflection;
2830
import com.tngtech.archunit.base.ResolvesTypesViaReflection;
2931
import com.tngtech.archunit.base.Suppliers;
32+
import com.tngtech.archunit.core.domain.properties.CanBeAnnotated;
33+
import com.tngtech.archunit.core.domain.properties.HasModifiers;
34+
import com.tngtech.archunit.core.domain.properties.HasName;
35+
import com.tngtech.archunit.core.domain.properties.HasOwner;
36+
import com.tngtech.archunit.core.domain.properties.HasParameterTypes;
37+
import com.tngtech.archunit.core.domain.properties.HasReturnType;
38+
import com.tngtech.archunit.core.domain.properties.HasThrowsClause;
3039
import com.tngtech.archunit.core.importer.DomainBuilders;
3140

3241
import static com.google.common.collect.Sets.union;
3342
import static com.tngtech.archunit.PublicAPI.Usage.ACCESS;
43+
import static com.tngtech.archunit.base.DescribedPredicate.describe;
3444
import static com.tngtech.archunit.core.domain.Formatters.formatMethod;
3545
import static com.tngtech.archunit.core.domain.properties.HasName.Utils.namesOf;
46+
import static java.util.stream.Collectors.toList;
3647

3748
@PublicAPI(usage = ACCESS)
3849
public final class JavaMethod extends JavaCodeUnit {
@@ -126,6 +137,30 @@ public String getDescription() {
126137
return "Method <" + getFullName() + ">";
127138
}
128139

140+
/**
141+
* Returns true, if this method overrides or implements a method from a supertype.
142+
* The annotation {@link Override} may or may not be present on this method.
143+
*
144+
* @see Override
145+
*/
146+
@PublicAPI(usage = ACCESS)
147+
public boolean isOverriding() {
148+
List<JavaClass> supertypes = Stream.concat(
149+
getOwner().getAllRawSuperclasses().stream(),
150+
getOwner().getAllRawInterfaces().stream()
151+
).collect(toList());
152+
153+
String name = getName();
154+
String[] parameterTypes = getRawParameterTypes().stream().map(JavaClass::getFullName).toArray(String[]::new);
155+
for (JavaClass supertype : supertypes) {
156+
if (supertype.tryGetMethod(name, parameterTypes).isPresent()) {
157+
return true;
158+
}
159+
}
160+
161+
return false;
162+
}
163+
129164
@ResolvesTypesViaReflection
130165
@MayResolveTypesViaReflection(reason = "Just part of a bigger resolution process")
131166
private class ReflectMethodSupplier implements Supplier<Method> {
@@ -141,4 +176,33 @@ public Method get() {
141176
}
142177
}
143178

179+
/**
180+
* Predefined {@link DescribedPredicate predicates} targeting {@link JavaMethod}.
181+
* Note that due to inheritance further predicates for {@link JavaMethod} can be found in the following locations:
182+
* <ul>
183+
* <li>{@link JavaCodeUnit.Predicates}</li>
184+
* <li>{@link JavaMember.Predicates}</li>
185+
* <li>{@link HasName.Predicates}</li>
186+
* <li>{@link HasName.AndFullName.Predicates}</li>
187+
* <li>{@link HasModifiers.Predicates}</li>
188+
* <li>{@link CanBeAnnotated.Predicates}</li>
189+
* <li>{@link HasOwner.Predicates}</li>
190+
* <li>{@link HasParameterTypes.Predicates}</li>
191+
* <li>{@link HasReturnType.Predicates}</li>
192+
* <li>{@link HasThrowsClause.Predicates}</li>
193+
* </ul>
194+
*/
195+
@PublicAPI(usage = ACCESS)
196+
public static final class Predicates {
197+
private Predicates() {
198+
}
199+
200+
/**
201+
* @see JavaMethod#isOverriding()
202+
*/
203+
@PublicAPI(usage = ACCESS)
204+
public static DescribedPredicate<JavaMethod> overriding() {
205+
return describe("overriding", JavaMethod::isOverriding);
206+
}
207+
}
144208
}

archunit/src/main/java/com/tngtech/archunit/lang/syntax/MethodsShouldInternal.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
import com.tngtech.archunit.lang.syntax.elements.MethodsShould;
2525
import com.tngtech.archunit.lang.syntax.elements.MethodsShouldConjunction;
2626

27+
import static com.tngtech.archunit.core.domain.JavaMethod.Predicates.overriding;
28+
import static com.tngtech.archunit.lang.conditions.ArchConditions.be;
29+
import static com.tngtech.archunit.lang.conditions.ArchConditions.not;
30+
2731
class MethodsShouldInternal
2832
extends AbstractCodeUnitsShouldInternal<JavaMethod, MethodsShouldInternal>
2933
implements MethodsShould<MethodsShouldInternal>, MethodsShouldConjunction {
@@ -58,4 +62,14 @@ private MethodsShouldInternal(
5862
MethodsShouldInternal copyWithNewCondition(ConditionAggregator<JavaMethod> newCondition) {
5963
return new MethodsShouldInternal(classesTransformer, priority, newCondition, prepareCondition);
6064
}
65+
66+
@Override
67+
public MethodsShouldInternal beOverriding() {
68+
return addCondition(be(overriding()));
69+
}
70+
71+
@Override
72+
public MethodsShouldInternal notBeOverriding() {
73+
return addCondition(not(be(overriding())));
74+
}
6175
}

archunit/src/main/java/com/tngtech/archunit/lang/syntax/MethodsThatInternal.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,33 @@
1515
*/
1616
package com.tngtech.archunit.lang.syntax;
1717

18+
import com.tngtech.archunit.base.DescribedPredicate;
1819
import com.tngtech.archunit.core.domain.JavaMethod;
1920
import com.tngtech.archunit.lang.syntax.elements.MethodsThat;
2021

22+
import static com.tngtech.archunit.base.DescribedPredicate.not;
23+
import static com.tngtech.archunit.core.domain.JavaMethod.Predicates.overriding;
24+
import static com.tngtech.archunit.lang.conditions.ArchPredicates.are;
25+
2126
class MethodsThatInternal
2227
extends CodeUnitsThatInternal<JavaMethod, GivenMethodsInternal>
2328
implements MethodsThat<GivenMethodsInternal> {
2429

2530
MethodsThatInternal(GivenMethodsInternal givenMethods, PredicateAggregator<JavaMethod> currentPredicate) {
2631
super(givenMethods, currentPredicate);
2732
}
33+
34+
@Override
35+
public GivenMethodsInternal areOverriding() {
36+
return withPredicate(are(overriding()));
37+
}
38+
39+
@Override
40+
public GivenMethodsInternal areNotOverriding() {
41+
return withPredicate(are(not(overriding())));
42+
}
43+
44+
private GivenMethodsInternal withPredicate(DescribedPredicate<JavaMethod> predicate) {
45+
return givenMembers.with(currentPredicate.add(predicate));
46+
}
2847
}

archunit/src/main/java/com/tngtech/archunit/lang/syntax/elements/MethodsShould.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.tngtech.archunit.lang.syntax.elements;
1717

1818
import com.tngtech.archunit.PublicAPI;
19+
import com.tngtech.archunit.core.domain.JavaMethod;
1920

2021
import static com.tngtech.archunit.PublicAPI.Usage.ACCESS;
2122

@@ -53,4 +54,22 @@ public interface MethodsShould<CONJUNCTION extends MethodsShouldConjunction> ext
5354
*/
5455
@PublicAPI(usage = ACCESS)
5556
CONJUNCTION notBeFinal();
57+
58+
/**
59+
* Asserts that methods override other methods.
60+
*
61+
* @return A syntax element that can either be used as working rule, or to continue specifying a more complex rule
62+
* @see JavaMethod#isOverriding()
63+
*/
64+
@PublicAPI(usage = ACCESS)
65+
CONJUNCTION beOverriding();
66+
67+
/**
68+
* Asserts that methods do not override other methods.
69+
*
70+
* @return A syntax element that can either be used as working rule, or to continue specifying a more complex rule
71+
* @see JavaMethod#isOverriding()
72+
*/
73+
@PublicAPI(usage = ACCESS)
74+
CONJUNCTION notBeOverriding();
5675
}

archunit/src/main/java/com/tngtech/archunit/lang/syntax/elements/MethodsThat.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.tngtech.archunit.lang.syntax.elements;
1717

1818
import com.tngtech.archunit.PublicAPI;
19+
import com.tngtech.archunit.core.domain.JavaMethod;
1920

2021
import static com.tngtech.archunit.PublicAPI.Usage.ACCESS;
2122

@@ -53,4 +54,22 @@ public interface MethodsThat<CONJUNCTION> extends CodeUnitsThat<CONJUNCTION> {
5354
*/
5455
@PublicAPI(usage = ACCESS)
5556
CONJUNCTION areNotFinal();
57+
58+
/**
59+
* Matches methods that override other methods.
60+
*
61+
* @return A syntax conjunction element, which can be completed to form a full rule
62+
* @see JavaMethod#isOverriding()
63+
*/
64+
@PublicAPI(usage = ACCESS)
65+
CONJUNCTION areOverriding();
66+
67+
/**
68+
* Matches methods that do not override other methods.
69+
*
70+
* @return A syntax conjunction element, which can be completed to form a full rule
71+
* @see JavaMethod#isOverriding()
72+
*/
73+
@PublicAPI(usage = ACCESS)
74+
CONJUNCTION areNotOverriding();
5675
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.tngtech.archunit.core.domain;
2+
3+
import org.junit.Test;
4+
5+
import static com.tngtech.archunit.core.domain.TestUtils.importMethod;
6+
import static org.assertj.core.api.Assertions.assertThat;
7+
8+
public class JavaMethodTest {
9+
10+
@Test
11+
public void isOverriding_returns_true_for_implemented_interface_method() {
12+
assertThat(importMethod(SomeClass.class, "doSomething").isOverriding()).isTrue();
13+
}
14+
15+
@Test
16+
public void isOverriding_returns_false_for_interface_method() {
17+
assertThat(importMethod(SomeInterface.class, "doSomething").isOverriding()).isFalse();
18+
}
19+
20+
@Test
21+
public void isOverriding_returns_true_for_overriding_method_of_java_lang_Object() {
22+
assertThat(importMethod(SomeClass.class, "toString").isOverriding()).isTrue();
23+
}
24+
25+
@Test
26+
public void isOverriding_returns_false_for_non_overriding_method() {
27+
assertThat(importMethod(SomeClass.class, "doSomethingElse").isOverriding()).isFalse();
28+
}
29+
30+
@Test
31+
public void isOverriding_returns_false_for_non_overriding_overloaded_method() {
32+
assertThat(importMethod(SomeClass.class, "doSomething", Object.class).isOverriding()).isFalse();
33+
}
34+
35+
@SuppressWarnings("unused")
36+
private interface SomeInterface {
37+
38+
void doSomething();
39+
}
40+
41+
@SuppressWarnings("unused")
42+
private static class SomeClass implements SomeInterface {
43+
44+
@Override
45+
public void doSomething() {
46+
}
47+
48+
public void doSomething(Object o) {
49+
}
50+
51+
public void doSomethingElse() {
52+
}
53+
54+
@Override
55+
public String toString() {
56+
return super.toString();
57+
}
58+
}
59+
}

archunit/src/test/java/com/tngtech/archunit/lang/syntax/elements/GivenMethodsTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public static Object[][] restricted_property_rule_starts() {
2727
$(described(methods().that().areNotFinal()), ImmutableSet.of(METHOD_C, METHOD_D)),
2828
$(described(methods().that().areStatic()), ImmutableSet.of(METHOD_B, METHOD_D)),
2929
$(described(methods().that().areNotStatic()), ImmutableSet.of(METHOD_A, METHOD_C)),
30+
$(described(methods().that().areOverriding()), ImmutableSet.of(METHOD_A)),
31+
$(described(methods().that().areNotOverriding()), ImmutableSet.of(METHOD_B, METHOD_C, METHOD_D)),
3032
$(described(methods().that().areFinal().and().areStatic()), ImmutableSet.of(METHOD_B)),
3133
$(described(methods().that().areFinal().or().areStatic()), ImmutableSet.of(METHOD_A, METHOD_B, METHOD_D))
3234
);
@@ -47,7 +49,7 @@ public void property_predicates(DescribedRuleStart ruleStart, Collection<String>
4749
private static final String METHOD_D = "methodD()";
4850

4951
@SuppressWarnings({"unused"})
50-
private static class ClassWithVariousMembers {
52+
private static class ClassWithVariousMembers extends SuperclassWithVariousMembers {
5153
public final void methodA(int[] array) {
5254
}
5355
protected static final void methodB(boolean flag) {
@@ -58,4 +60,10 @@ static int methodD() {
5860
return 0;
5961
}
6062
}
63+
64+
@SuppressWarnings("unused")
65+
private static class SuperclassWithVariousMembers {
66+
public void methodA(int[] array) {
67+
}
68+
}
6169
}

archunit/src/test/java/com/tngtech/archunit/lang/syntax/elements/MethodsShouldTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public static Object[][] restricted_property_rule_ends() {
2929
$(methods().should().notBeFinal(), ImmutableSet.of(METHOD_A, METHOD_B)),
3030
$(methods().should().beStatic(), ImmutableSet.of(METHOD_A, METHOD_C)),
3131
$(methods().should().notBeStatic(), ImmutableSet.of(METHOD_B, METHOD_D)),
32+
$(methods().should().beOverriding(), ImmutableSet.of(METHOD_B, METHOD_C, METHOD_D)),
33+
$(methods().should().notBeOverriding(), ImmutableSet.of(METHOD_A)),
3234
$(methods().should().notBeFinal().andShould().notBeStatic(), ImmutableSet.of(METHOD_A, METHOD_B, METHOD_D)),
3335
$(methods().should().notBeFinal().orShould().notBeStatic(), ImmutableSet.of(METHOD_B))
3436
);
@@ -49,7 +51,7 @@ public void property_predicates(ArchRule ruleStart, Collection<String> expectedM
4951
private static final String METHOD_D = "methodD()";
5052

5153
@SuppressWarnings({"unused"})
52-
private static class ClassWithVariousMembers {
54+
private static class ClassWithVariousMembers extends SuperclassWithVariousMembers {
5355
public final void methodA(int[] array) {
5456
}
5557
protected static final void methodB(boolean flag) {
@@ -60,4 +62,10 @@ static int methodD() {
6062
return 0;
6163
}
6264
}
65+
66+
@SuppressWarnings("unused")
67+
private static class SuperclassWithVariousMembers {
68+
public void methodA(int[] array) {
69+
}
70+
}
6371
}

0 commit comments

Comments
 (0)