Skip to content

Commit

Permalink
Provide excluding classes globally and per rule
Browse files Browse the repository at this point in the history
Closes gh-43
Closes gh-49
  • Loading branch information
mnhock committed Jun 21, 2024
1 parent 48a65b2 commit 674f360
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 32 deletions.
17 changes: 16 additions & 1 deletion docs/USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ Taikai.builder()
.check();
```

### 3.3 Excluding Classes Globally

You can globally exclude specific classes from all rule checks by using the `excludeClass` or `excludeClasses` methods in the builder. This ensures that the specified classes are not checked by any rule.

```java
Taikai.builder()
.namespace("com.company.yourproject")
.excludeClass("com.company.yourproject.SomeClassToExclude")
.excludeClasses(Set.of("com.company.yourproject.foo.ClassToExclude", "com.company.yourproject.bar.ClassToExclude"))
.build()
.check();
```

## 4. Rules Overview

Taikai's architecture rules cover a wide range of categories to enforce best practices and maintain consistency.
Expand Down Expand Up @@ -455,13 +468,15 @@ Taikai.builder()

### Custom Configuration for Import Rules

For every rule, you have the flexibility to add a custom configuration. This allows you to specify the namespace and import options tailored to your needs.
For every rule, you have the flexibility to add a custom configuration. This allows you to specify the namespace, import options, and exclude specific classes from the checks.

The `Configuration` class offers various static methods to create custom configurations:
- `Configuration.of(String namespace)` to set a custom namespace.
- `Configuration.of(Namespace.IMPORT namespaceImport)` to specify import options such as `WITHOUT_TESTS`, `WITH_TESTS`, or `ONLY_TESTS`.
- `Configuration.of(String namespace, Namespace.IMPORT namespaceImport)` to set both namespace and import options.
- `Configuration.of(JavaClasses javaClasses)` to directly provide a set of Java classes.
- `Configuration.of(Set<String> excludedClasses)` to exclude specific classes from the checks.
- Additional overloaded methods to combine these options in various ways.

If a `namespaceImport` is not explicitly provided, it defaults to `Namespace.IMPORT.WITHOUT_TESTS`.

Expand Down
13 changes: 7 additions & 6 deletions src/main/java/com/enofex/taikai/Namespace.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.enofex.taikai;

import static java.util.Objects.requireNonNull;

import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption;
import java.util.Objects;

public final class Namespace {

Expand All @@ -17,8 +18,8 @@ private Namespace() {
}

public static JavaClasses from(String namespace, IMPORT importOption) {
Objects.requireNonNull(namespace);
Objects.requireNonNull(importOption);
requireNonNull(namespace);
requireNonNull(importOption);

return switch (importOption) {
case WITH_TESTS -> withTests(namespace);
Expand All @@ -28,7 +29,7 @@ public static JavaClasses from(String namespace, IMPORT importOption) {
}

public static JavaClasses withoutTests(String namespace) {
Objects.requireNonNull(namespace);
requireNonNull(namespace);

return new ClassFileImporter()
.withImportOption(new ImportOption.DoNotIncludeTests())
Expand All @@ -37,15 +38,15 @@ public static JavaClasses withoutTests(String namespace) {
}

public static JavaClasses withTests(String namespace) {
Objects.requireNonNull(namespace);
requireNonNull(namespace);

return new ClassFileImporter()
.withImportOption(new ImportOption.DoNotIncludeJars())
.importPackages(namespace);
}

public static JavaClasses onlyTests(String namespace) {
Objects.requireNonNull(namespace);
requireNonNull(namespace);

return new ClassFileImporter()
.withImportOption(new ImportOption.OnlyIncludeTests())
Expand Down
31 changes: 27 additions & 4 deletions src/main/java/com/enofex/taikai/Taikai.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.enofex.taikai;

import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNullElse;

import com.enofex.taikai.configures.Configurer;
import com.enofex.taikai.configures.ConfigurerContext;
import com.enofex.taikai.configures.Configurers;
Expand All @@ -12,19 +15,23 @@
import com.tngtech.archunit.core.domain.JavaClasses;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;

public final class Taikai {

private final boolean failOnEmpty;
private final String namespace;
private final Set<String> excludedClasses;
private final Collection<TaikaiRule> rules;

private Taikai(Builder builder) {
this.failOnEmpty = builder.failOnEmpty;
this.namespace = builder.namespace;
this.excludedClasses = requireNonNullElse(builder.excludedClasses, Collections.emptySet());
this.rules = Stream.concat(
builder.configurers.all()
.stream()
Expand Down Expand Up @@ -52,12 +59,16 @@ public JavaClasses classesWithTests() {
return Namespace.withTests(this.namespace);
}

public Set<String> excludedClasses() {
return this.excludedClasses;
}

public Collection<TaikaiRule> rules() {
return this.rules;
}

public void check() {
this.rules.forEach(rule -> rule.check(this.namespace));
this.rules.forEach(rule -> rule.check(this.namespace, this.excludedClasses));
}

public static Builder builder() {
Expand All @@ -68,13 +79,15 @@ public static final class Builder {

private final Configurers configurers;
private final Collection<TaikaiRule> rules;
private final Set<String> excludedClasses;

private boolean failOnEmpty;
private String namespace;

public Builder() {
this.configurers = new Configurers();
this.rules = new ArrayList<>();
this.excludedClasses = new HashSet<>();
}

public Builder addRule(TaikaiRule rule) {
Expand All @@ -97,6 +110,16 @@ public Builder namespace(String namespace) {
return this;
}

public Builder excludeClass(String className) {
this.excludedClasses.add(className);
return this;
}

public Builder excludeClasses(Collection<String> classNames) {
this.excludedClasses.addAll(classNames);
return this;
}

public Builder java(Customizer<JavaConfigurer> customizer) {
return configure(customizer, JavaConfigurer::new);
}
Expand All @@ -115,8 +138,8 @@ public Builder spring(Customizer<SpringConfigurer> customizer) {

private <T extends Configurer> Builder configure(Customizer<T> customizer,
Function<ConfigurerContext, T> supplier) {
Objects.requireNonNull(customizer);
Objects.requireNonNull(supplier);
requireNonNull(customizer);
requireNonNull(supplier);

customizer.customize(this.configurers.getOrApply(supplier.apply(
new ConfigurerContext(this.namespace, this.configurers)))
Expand Down
84 changes: 72 additions & 12 deletions src/main/java/com/enofex/taikai/TaikaiRule.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
package com.enofex.taikai;

import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNullElse;

import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.lang.ArchRule;
import java.util.Objects;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class TaikaiRule {

private final ArchRule archRule;
private final Configuration configuration;

private TaikaiRule(ArchRule archRule, Configuration configuration) {
this.archRule = Objects.requireNonNull(archRule);
this.configuration = Objects.requireNonNullElse(configuration,
Configuration.defaultConfiguration());
this.archRule = requireNonNull(archRule);
this.configuration = requireNonNullElse(configuration, Configuration.defaultConfiguration());
}

public ArchRule archRule() {
Expand All @@ -32,6 +39,10 @@ public static TaikaiRule of(ArchRule archRule, Configuration configuration) {
}

public void check(String globalNamespace) {
check(globalNamespace, Collections.emptySet());
}

public void check(String globalNamespace, Set<String> excludedClasses) {
if (this.configuration.javaClasses() != null) {
this.archRule.check(this.configuration.javaClasses());
} else {
Expand All @@ -43,22 +54,44 @@ public void check(String globalNamespace) {
throw new TaikaiException("Namespace is not provided");
}

this.archRule.check(Namespace.from(namespace, this.configuration.namespaceImport));
Set<String> allExcludedClasses = allExcludedClasses(excludedClasses);

if (allExcludedClasses.isEmpty()) {
this.archRule.check(Namespace.from(namespace, this.configuration.namespaceImport));
} else {
this.archRule.check(Namespace.from(namespace, this.configuration.namespaceImport)
.that(new DescribedPredicate<>("exclude classes") {
@Override
public boolean test(JavaClass javaClass) {
return !allExcludedClasses.contains(javaClass.getFullName());
}
}));
}
}
}

private Set<String> allExcludedClasses(Set<String> excludedClasses) {
return Stream.concat(
this.configuration.excludedClasses != null
? this.configuration.excludedClasses.stream() : Stream.empty(),
excludedClasses != null
? excludedClasses.stream() : Stream.empty()).collect(Collectors.toSet());
}

public static final class Configuration {

private final String namespace;
private final Namespace.IMPORT namespaceImport;
private final JavaClasses javaClasses;
private final Set<String> excludedClasses;

private Configuration(String namespace, Namespace.IMPORT namespaceImport,
JavaClasses javaClasses) {
JavaClasses javaClasses, Set<String> excludedClasses) {
this.namespace = namespace;
this.namespaceImport = Objects.requireNonNullElse(namespaceImport,
this.namespaceImport = requireNonNullElse(namespaceImport,
Namespace.IMPORT.WITHOUT_TESTS);
this.javaClasses = javaClasses;
this.excludedClasses = excludedClasses != null ? excludedClasses : Collections.emptySet();
}

public String namespace() {
Expand All @@ -73,24 +106,51 @@ public JavaClasses javaClasses() {
return this.javaClasses;
}

public Set<String> excludedClasses() {
return this.excludedClasses;
}

public static Configuration defaultConfiguration() {
return new Configuration(null, Namespace.IMPORT.WITHOUT_TESTS, null);
return new Configuration(null, Namespace.IMPORT.WITHOUT_TESTS, null, null);
}

public static Configuration of(String namespace) {
return new Configuration(namespace, Namespace.IMPORT.WITHOUT_TESTS, null);
return new Configuration(namespace, Namespace.IMPORT.WITHOUT_TESTS, null, null);
}

public static Configuration of(String namespace, Set<String> excludedClasses) {
return new Configuration(namespace, Namespace.IMPORT.WITHOUT_TESTS, null, excludedClasses);
}

public static Configuration of(Namespace.IMPORT namespaceImport) {
return new Configuration(null, namespaceImport, null);
return new Configuration(null, namespaceImport, null, null);
}

public static Configuration of(Namespace.IMPORT namespaceImport, Set<String> excludedClasses) {
return new Configuration(null, namespaceImport, null, excludedClasses);
}

public static Configuration of(String namespace, Namespace.IMPORT namespaceImport) {
return new Configuration(namespace, namespaceImport, null);
return new Configuration(namespace, namespaceImport, null, null);
}

public static Configuration of(String namespace, Namespace.IMPORT namespaceImport,
Set<String> excludedClasses) {
return new Configuration(namespace, namespaceImport, null, excludedClasses);
}

public static Configuration of(JavaClasses javaClasses) {
return new Configuration(null, null, javaClasses);
return new Configuration(null, null, javaClasses, null);
}

public static Configuration of(JavaClasses javaClasses, Set<String> excludedClasses) {
return new Configuration(null, null, javaClasses, excludedClasses);
}

public static Configuration of(Set<String> excludedClasses) {
return new Configuration(null, null, null, excludedClasses);
}


}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.enofex.taikai.configures;

import static java.util.Objects.requireNonNull;

import com.enofex.taikai.TaikaiRule;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
import java.util.function.Supplier;

public abstract class AbstractConfigurer implements Configurer {
Expand All @@ -12,7 +13,7 @@ public abstract class AbstractConfigurer implements Configurer {
private final Collection<TaikaiRule> rules;

protected AbstractConfigurer(ConfigurerContext configurerContext) {
this.configurerContext = Objects.requireNonNull(configurerContext);
this.configurerContext = requireNonNull(configurerContext);
this.rules = new ArrayList<>();
}

Expand All @@ -37,8 +38,8 @@ protected <T extends Configurer> void disable(Class<T> clazz) {

protected <T extends Configurer, C extends Configurer> C customizer(Customizer<T> customizer,
Supplier<T> supplier) {
Objects.requireNonNull(customizer);
Objects.requireNonNull(supplier);
requireNonNull(customizer);
requireNonNull(supplier);

customizer.customize(this.configurerContext
.configurers()
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/enofex/taikai/configures/Configurers.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.enofex.taikai.configures;

import static java.util.Objects.requireNonNull;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

public final class Configurers implements Iterable<Configurer> {

Expand All @@ -15,7 +16,7 @@ public Configurers() {
}

public <C extends Configurer> C getOrApply(C configurer) {
Objects.requireNonNull(configurer);
requireNonNull(configurer);

C existingConfigurer = (C) this.get(configurer.getClass());
return existingConfigurer != null ? existingConfigurer : this.apply(configurer);
Expand Down
Loading

0 comments on commit 674f360

Please sign in to comment.