Skip to content

Commit

Permalink
Allow to use wildcard characters in certain places within a package part
Browse files Browse the repository at this point in the history
  • Loading branch information
skuzzle committed Sep 25, 2024
1 parent 3bf9a1b commit 3102485
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 67 deletions.
13 changes: 7 additions & 6 deletions readme/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -715,19 +715,20 @@ considerably slower.
## Package Patterns

Package patterns are dot separated strings that can be compared case sensitively part by part. Every part must adhere to
the java identifier rules with the exception of a some special literals:
the java identifier rules except some special literals:

1. `*` matches every package part but exactly one.
2. `**` matches multiple package parts but at least one.
3. `'*'` matches a literal `*` in an import statement.
4. `*SomeString` matches every package part that ends with `SomeString`.
5. `SomeString*` matches every package part that starts with `SomeString`.
6. `*SomeString*` matches every package part that contains `SomeString`.

The pattern `java.util.*` matches `java.util.ArrayList` but not `java.util.regex.Pattern`.

Likewise the pattern `java.util.**` matches all classes and subclasses contained in
`java.util`. Double wildcards are supported everywhere within a pattern. `**.DumbName`
would match every import which ends in `DumbName`. Wildcards are forbidden to be used in
combination with other characters within a single part, like in `com.foo**`. Also parts
within a package must not be empty like in `foo..bar`.
Likewise, the pattern `java.util.**` matches all classes and subclasses contained in`java.util`.
Double wildcards are supported everywhere within a pattern. `**.*DumbName` would match every import which ends in `DumbName`.
Parts within a package must not be empty like in `foo..bar`.

If a pattern does not contain any wildcards, matching degrades to a simple String
comparison.
Expand Down
9 changes: 2 additions & 7 deletions readme/RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
[![Maven Central](https://img.shields.io/static/v1?label=MavenCentral&message=@project.version@&color=blue)](https://search.maven.org/artifact/@project.groupId@/restrict-imports-enforcer-rule/@project.version@/jar)
[![Gradle Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/@project.pluginId@?versionSuffix=@project.version@)](https://plugins.gradle.org/plugin/@project.pluginId@/@project.version@)

> [!NOTE]
> This is the first release after migrating our build to Gradle and which uses shaded dependencies.
> If you encounter any irregularities with this version, please do not hesitate to file an issue.
### Features
* [#38](https://github.com/skuzzle/restrict-imports-enforcer-rule/issues/38) Dependencies are shaded into plugin artifacts
* [#59](https://github.com/skuzzle/restrict-imports-enforcer-rule/issues/59) Provide a Gradle plugin
* [#118](https://github.com/skuzzle/restrict-imports-enforcer-rule/issues/118) Print absolute paths in exception messages to make IntelliJ render clickable links
* [#177](https://github.com/skuzzle/restrict-imports-enforcer-rule/issues/177) Support matching prefix, suffix and infix
parts within a package pattern.

### Dependency coordinates
<details>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ private void checkCharacters(String original, String part, int partIndex) {

if (part.isEmpty()) {
throw new IllegalArgumentException(String.format("The pattern '%s' contains an empty part", original));
} else if ("*".equals(part) || "**".equals(part) || "'*'".equals(part)) {
} else if (isValidWildcardPart(part)) {
return;
} else if (part.contains("*")) {
} else if (part.contains("*") && (part.indexOf('*') > 1 || part.indexOf('*') < part.length() - 2)) {
throw new IllegalArgumentException(String.format(
"The pattern '%s' contains a part which mixes wildcards and normal characters", original));
} else if (partIndex == 0 && "static".equals(part)) {
Expand All @@ -91,6 +91,17 @@ private void checkCharacters(String original, String part, int partIndex) {
}
}

private boolean isValidWildcardPart(String patternPart) {
if ("*".equals(patternPart) || "**".equals(patternPart) || "'*'".equals(patternPart)) {
return true;
}
if (patternPart.startsWith("*") || patternPart.endsWith("*")) {
final String substring = patternPart.substring(1, patternPart.length() - 1);
return !substring.contains("*");
}
return false;
}

/**
* Parses each string of the given collection into a {@link PackagePattern} and
* returns them in a list.
Expand Down Expand Up @@ -193,6 +204,15 @@ private static boolean matchParts(String patternPart, String matchPart) {
return true;
} else if ("'*'".equals(patternPart)) {
return matchPart.equals("*");
} else if (patternPart.startsWith("*") && patternPart.endsWith("*")) {
final String infix = patternPart.substring(1, patternPart.length() - 1);
return matchPart.contains(infix);
} else if (patternPart.startsWith("*")) {
final String suffix = patternPart.substring(1);
return matchPart.endsWith(suffix);
} else if (patternPart.endsWith("*")) {
final String prefix = patternPart.substring(0, patternPart.length() - 1);
return matchPart.startsWith(prefix);
}
return patternPart.equals(matchPart);
}
Expand Down Expand Up @@ -228,7 +248,7 @@ public int compareTo(PackagePattern other) {
private int specificityOf(String part) {
if (part.equals("**")) {
return 0;
} else if (part.equals("*")) {
} else if (part.contains("*")) {
return 1;
}
return 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ private static SpecifityTest expect(String moreSpecific) {
expect("*").toBeMoreSpecificThan("**"),
expect("de.**").toBeMoreSpecificThan("**"),
expect("de.xyz.*").toBeMoreSpecificThan("de.xyz.**"),
expect("de.xyz.Foo").toBeMoreSpecificThan("de.xyz.Foo*"),
expect("de.xyz.Foo").toBeMoreSpecificThan("de.xyz.*Foo"),
expect("de.xyz.Foo").toBeMoreSpecificThan("de.xyz.*Foo*"),
expect("de.xyz.*Foo").toBeMoreSpecificThan("de.*xyz.*Foo"),
expect("de.x.y.z.*Foo").toBeMoreSpecificThan("de.*xyz.*Foo"),

expect("de.*.xyz").toBeMoreSpecificThan("de.**.xyz"),
expect("de").toBeMoreSpecificThan("*"),
expect("de").toBeMoreSpecificThan("**"),
Expand All @@ -50,7 +56,7 @@ private static SpecifityTest expect(String moreSpecific) {
expect("*.xyz").toBeMoreSpecificThan("**.xyz"));

@TestFactory
Stream<DynamicNode> testCompareToSelf() throws Exception {
Stream<DynamicNode> testCompareToSelf() {
return patterns.stream()
.map(pattern -> DynamicTest.dynamicTest(String.format(
"%s should be more specific than itself", pattern.moreSpecific),
Expand All @@ -64,7 +70,7 @@ Stream<DynamicNode> testCompareToSelf() throws Exception {
}

@TestFactory
Stream<DynamicNode> testCompareSpecificty() throws Exception {
Stream<DynamicNode> testCompareSpecificty() {
return patterns.stream()
.map(pattern -> DynamicTest.dynamicTest(String.format(
"%s should be more specific than %s", pattern.moreSpecific,
Expand All @@ -80,7 +86,7 @@ Stream<DynamicNode> testCompareSpecificty() throws Exception {
}

@TestFactory
Stream<DynamicNode> testCompareSpecifictyReverse() throws Exception {
Stream<DynamicNode> testCompareSpecifictyReverse() {
return patterns.stream()
.map(pattern -> DynamicTest.dynamicTest(String.format(
"%s should not be more specific than %s", pattern.lessSpecific,
Expand Down
Loading

0 comments on commit 3102485

Please sign in to comment.