From dbcfc79eb5c220737ddc38daadafa17dd20e36b8 Mon Sep 17 00:00:00 2001 From: Boris van Katwijk Date: Wed, 1 Jan 2025 12:05:09 +0100 Subject: [PATCH 1/5] Create ClassSectionTest.java --- src/test/java/linter/ClassSectionTest.java | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/test/java/linter/ClassSectionTest.java diff --git a/src/test/java/linter/ClassSectionTest.java b/src/test/java/linter/ClassSectionTest.java new file mode 100644 index 000000000..160adfd59 --- /dev/null +++ b/src/test/java/linter/ClassSectionTest.java @@ -0,0 +1,70 @@ +package linter; + +import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.domain.JavaMethod; +import com.tngtech.archunit.core.domain.JavaModifier; +import com.tngtech.archunit.core.importer.ClassFileImporter; +import com.tngtech.archunit.core.importer.ImportOption; +import com.tngtech.archunit.lang.ArchCondition; +import com.tngtech.archunit.lang.ConditionEvents; +import com.tngtech.archunit.lang.SimpleConditionEvent; +import org.junit.jupiter.api.Test; + +import java.util.*; +import java.util.stream.Collectors; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; + +public class ClassSectionTest { + @Test + public void methodsAreAlphabeticallyOrderedInSections() { + classes() + .should(haveOrderedMethodSections()).check(new ClassFileImporter() + .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) + .importPackages("io.vavr")); + } + + private ArchCondition haveOrderedMethodSections() { + return new ArchCondition("have methods alphabetically ordered in sections") { + @Override + public void check(JavaClass javaClass, ConditionEvents events) { + Map> sections = methodSections(javaClass.getMethods()); + + for (Map.Entry> section : sections.entrySet()) { + List methods = section.getValue(); + + List methodNames = methods.stream() + .map(JavaMethod::getName) + .sorted() + .collect(Collectors.toList()); + + List methodOrder = methods.stream() + .sorted(Comparator.comparing(m -> m.getSourceCodeLocation().getLineNumber())) + .map(JavaMethod::getName) + .collect(Collectors.toList()); + + if (!methodOrder.equals(methodNames)) { + String message = String.format("Methods in section '%s' of class '%s' are not alphabetically ordered: %s", + section.getKey(), javaClass.getName(), methodNames); + events.add(SimpleConditionEvent.violated(javaClass, message)); + } + } + } + }; + } + + private Map> methodSections(Set methods) { + Map> sections = new HashMap<>(); + sections.put("static API", new ArrayList<>()); + sections.put("non-static API", new ArrayList<>()); + + for (JavaMethod method : methods) { + if (method.getModifiers().contains(JavaModifier.STATIC)) { + sections.get("static API").add(method); + } else { + sections.get("non-static API").add(method); + } + } + return sections; + } +} From 619b4b231f646d916377b3be757f170746f2ce6b Mon Sep 17 00:00:00 2001 From: Boris van Katwijk Date: Wed, 1 Jan 2025 12:54:22 +0100 Subject: [PATCH 2/5] Output actual order instead of alphabetical --- src/test/java/linter/ClassSectionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/linter/ClassSectionTest.java b/src/test/java/linter/ClassSectionTest.java index 160adfd59..fc78cf622 100644 --- a/src/test/java/linter/ClassSectionTest.java +++ b/src/test/java/linter/ClassSectionTest.java @@ -45,7 +45,7 @@ public void check(JavaClass javaClass, ConditionEvents events) { if (!methodOrder.equals(methodNames)) { String message = String.format("Methods in section '%s' of class '%s' are not alphabetically ordered: %s", - section.getKey(), javaClass.getName(), methodNames); + section.getKey(), javaClass.getName(), methodOrder); events.add(SimpleConditionEvent.violated(javaClass, message)); } } From 0b84d99ae19e20b49e9c366f5f7dac8d04952666 Mon Sep 17 00:00:00 2001 From: Boris van Katwijk Date: Wed, 1 Jan 2025 13:09:17 +0100 Subject: [PATCH 3/5] Update ClassSectionTest.java --- src/test/java/linter/ClassSectionTest.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/test/java/linter/ClassSectionTest.java b/src/test/java/linter/ClassSectionTest.java index fc78cf622..4c309e7b5 100644 --- a/src/test/java/linter/ClassSectionTest.java +++ b/src/test/java/linter/ClassSectionTest.java @@ -16,6 +16,11 @@ import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; public class ClassSectionTest { + + public static final String STATIC_API = "static API"; + public static final String ADJUSTED_RETURN_TYPES = "adjusted return types"; + public static final String NON_STATIC_API = "non-static API"; + @Test public void methodsAreAlphabeticallyOrderedInSections() { classes() @@ -55,14 +60,17 @@ public void check(JavaClass javaClass, ConditionEvents events) { private Map> methodSections(Set methods) { Map> sections = new HashMap<>(); - sections.put("static API", new ArrayList<>()); - sections.put("non-static API", new ArrayList<>()); + sections.put(STATIC_API, new ArrayList<>()); + sections.put(ADJUSTED_RETURN_TYPES, new ArrayList<>()); + sections.put(NON_STATIC_API, new ArrayList<>()); for (JavaMethod method : methods) { if (method.getModifiers().contains(JavaModifier.STATIC)) { - sections.get("static API").add(method); + sections.get(STATIC_API).add(method); + } else if (method.isAnnotatedWith(Override.class)) { + sections.get(ADJUSTED_RETURN_TYPES).add(method); } else { - sections.get("non-static API").add(method); + sections.get(NON_STATIC_API).add(method); } } return sections; From 21d0fb64320ffe8e946197754e2a0e420f4800b6 Mon Sep 17 00:00:00 2001 From: Boris van Katwijk Date: Wed, 1 Jan 2025 13:27:43 +0100 Subject: [PATCH 4/5] Update ClassSectionTest.java --- src/test/java/linter/ClassSectionTest.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/test/java/linter/ClassSectionTest.java b/src/test/java/linter/ClassSectionTest.java index 4c309e7b5..475acfa26 100644 --- a/src/test/java/linter/ClassSectionTest.java +++ b/src/test/java/linter/ClassSectionTest.java @@ -3,6 +3,7 @@ import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaMethod; import com.tngtech.archunit.core.domain.JavaModifier; +import com.tngtech.archunit.core.domain.JavaParameter; import com.tngtech.archunit.core.importer.ClassFileImporter; import com.tngtech.archunit.core.importer.ImportOption; import com.tngtech.archunit.lang.ArchCondition; @@ -67,12 +68,22 @@ private Map> methodSections(Set methods) { for (JavaMethod method : methods) { if (method.getModifiers().contains(JavaModifier.STATIC)) { sections.get(STATIC_API).add(method); - } else if (method.isAnnotatedWith(Override.class)) { + } else if (isOverridden(method)) { sections.get(ADJUSTED_RETURN_TYPES).add(method); - } else { + } + else { sections.get(NON_STATIC_API).add(method); } } return sections; } + + private boolean isOverridden(JavaMethod method) { + return method.getOwner().getAllRawInterfaces().stream() + .anyMatch(c -> c.tryGetMethod(method.getName(), getTypeNames(method.getParameters())).isPresent()); + } + + private String[] getTypeNames(List parameters) { + return parameters.stream().map(p -> p.getRawType().getName()).toArray(String[]::new); + } } From 7039f1e819eedc0f032425a17e7a872aca99a060 Mon Sep 17 00:00:00 2001 From: Boris van Katwijk Date: Wed, 1 Jan 2025 13:30:38 +0100 Subject: [PATCH 5/5] Update ClassSectionTest.java --- src/test/java/linter/ClassSectionTest.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/test/java/linter/ClassSectionTest.java b/src/test/java/linter/ClassSectionTest.java index 475acfa26..358f06fc2 100644 --- a/src/test/java/linter/ClassSectionTest.java +++ b/src/test/java/linter/ClassSectionTest.java @@ -25,7 +25,8 @@ public class ClassSectionTest { @Test public void methodsAreAlphabeticallyOrderedInSections() { classes() - .should(haveOrderedMethodSections()).check(new ClassFileImporter() + .should(haveOrderedMethodSections()) + .check(new ClassFileImporter() .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) .importPackages("io.vavr")); } @@ -70,20 +71,25 @@ private Map> methodSections(Set methods) { sections.get(STATIC_API).add(method); } else if (isOverridden(method)) { sections.get(ADJUSTED_RETURN_TYPES).add(method); - } - else { + } else { sections.get(NON_STATIC_API).add(method); } } return sections; } + /* Source: https://github.com/TNG/ArchUnit/issues/359#issuecomment-975456116 */ private boolean isOverridden(JavaMethod method) { - return method.getOwner().getAllRawInterfaces().stream() + return method.getOwner() + .getAllRawInterfaces() + .stream() .anyMatch(c -> c.tryGetMethod(method.getName(), getTypeNames(method.getParameters())).isPresent()); } + /* Source: https://github.com/TNG/ArchUnit/issues/359#issuecomment-975456116 */ private String[] getTypeNames(List parameters) { - return parameters.stream().map(p -> p.getRawType().getName()).toArray(String[]::new); + return parameters.stream() + .map(p -> p.getRawType().getName()) + .toArray(String[]::new); } }