Skip to content

Commit bc40b10

Browse files
authored
chore: refactor to use same glob as Go (#23)
Implement the `Glob` class in Java to be the same a [Go](https://github.com/cloudquery/plugin-sdk/blob/main/glob/glob.go) to make sure we have parity between Java and Go with the globbing behaviour.
1 parent 1e4a0fc commit bc40b10

File tree

7 files changed

+177
-78
lines changed

7 files changed

+177
-78
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.cloudquery.glob;
2+
3+
public class Glob {
4+
public static final String GLOB = "*";
5+
6+
public static boolean match(String pattern, String subject) {
7+
if (pattern.isEmpty()) {
8+
return subject.equals(pattern);
9+
}
10+
11+
if (pattern.equals(GLOB)) {
12+
return true;
13+
}
14+
15+
String[] parts = pattern.split("\\" + GLOB, -1);
16+
if (parts.length == 1) {
17+
return subject.equals(pattern);
18+
}
19+
20+
boolean leadingGlob = pattern.startsWith(GLOB);
21+
boolean trailingGlob = pattern.endsWith(GLOB);
22+
int end = parts.length - 1;
23+
24+
for (int i = 0; i < end; i++) {
25+
int idx = subject.indexOf(parts[i]);
26+
27+
if (i == 0) {
28+
if (!leadingGlob && idx != 0) {
29+
return false;
30+
}
31+
} else {
32+
if (idx < 0) {
33+
return false;
34+
}
35+
}
36+
37+
subject = subject.substring(idx + parts[i].length());
38+
}
39+
40+
return trailingGlob || subject.endsWith(parts[end]);
41+
}
42+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2014 Ryan Uber
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Glob Matching Library
2+
3+
This glob-matching library was copied from [ryanuber/go-glob](https://github.com/ryanuber/go-glob) and therefore falls under its [license](LICENSE).

lib/src/main/java/io/cloudquery/helper/GlobMatcher.java

Lines changed: 0 additions & 23 deletions
This file was deleted.

lib/src/main/java/io/cloudquery/schema/Table.java

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.cloudquery.schema;
22

3-
import io.cloudquery.helper.GlobMatcher;
3+
import io.cloudquery.glob.Glob;
44
import lombok.Builder;
55
import lombok.Getter;
66

@@ -28,47 +28,44 @@ public static List<Table> flattenTables(List<Table> tables) {
2828
}
2929

3030
public static List<Table> filterDFS(List<Table> tables, List<String> includeConfiguration, List<String> skipConfiguration, boolean skipDependentTables) throws SchemaException {
31-
List<GlobMatcher> includes = includeConfiguration.stream().map(GlobMatcher::new).toList();
32-
List<GlobMatcher> excludes = skipConfiguration.stream().map(GlobMatcher::new).toList();
33-
3431
List<Table> flattenedTables = flattenTables(tables);
35-
for (GlobMatcher includeMatcher : includes) {
32+
for (String includePattern : includeConfiguration) {
3633
boolean includeMatch = false;
3734
for (Table table : flattenedTables) {
38-
if (includeMatcher.matches(table.getName())) {
35+
if (Glob.match(includePattern, table.getName())) {
3936
includeMatch = true;
4037
break;
4138
}
4239
}
4340
if (!includeMatch) {
44-
throw new SchemaException("table configuration includes a pattern \"" + includeMatcher.getStringMatch() + "\" with no matches");
41+
throw new SchemaException("table configuration includes a pattern \"" + includePattern + "\" with no matches");
4542
}
4643
}
47-
for (GlobMatcher excludeMatcher : excludes) {
44+
for (String excludePattern : skipConfiguration) {
4845
boolean excludeMatch = false;
4946
for (Table table : flattenedTables) {
50-
if (excludeMatcher.matches(table.getName())) {
47+
if (Glob.match(excludePattern, table.getName())) {
5148
excludeMatch = true;
5249
break;
5350
}
5451
}
5552
if (!excludeMatch) {
56-
throw new SchemaException("skip configuration includes a pattern \"" + excludeMatcher.getStringMatch() + "\" with no matches");
53+
throw new SchemaException("skip configuration includes a pattern \"" + excludePattern + "\" with no matches");
5754
}
5855
}
5956

6057
Predicate<Table> include = table -> {
61-
for (GlobMatcher matcher : includes) {
62-
if (matcher.matches(table.getName())) {
58+
for (String includePattern : includeConfiguration) {
59+
if (Glob.match(includePattern, table.getName())) {
6360
return true;
6461
}
6562
}
6663
return false;
6764
};
6865

6966
Predicate<Table> exclude = table -> {
70-
for (GlobMatcher matcher : excludes) {
71-
if (matcher.matches(table.getName())) {
67+
for (String excludePattern : skipConfiguration) {
68+
if (Glob.match(excludePattern, table.getName())) {
7269
return true;
7370
}
7471
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package io.cloudquery.glob;
2+
3+
import org.junit.Test;
4+
5+
import java.util.List;
6+
7+
import static io.cloudquery.glob.Glob.GLOB;
8+
import static org.junit.Assert.assertFalse;
9+
import static org.junit.Assert.assertTrue;
10+
11+
public class GlobTest {
12+
@Test
13+
public void testEmptyPattern() {
14+
assertGlobMatch("", "");
15+
assertNotGlobMatch("", "test");
16+
}
17+
18+
@Test
19+
public void testEmptySubject() {
20+
for (String s : List.of("",
21+
"*",
22+
"**",
23+
"***",
24+
"****************",
25+
GLOB.repeat(1000000)
26+
)) {
27+
assertGlobMatch(s, "");
28+
}
29+
30+
for (String pattern : List.of(
31+
// No globs/non-glob characters
32+
"test",
33+
"*test*",
34+
35+
// Trailing characters
36+
"*x",
37+
"*****************x",
38+
GLOB.repeat(1000000) + "x",
39+
40+
// Leading characters
41+
"x*",
42+
"x*****************",
43+
"x" + GLOB.repeat(1000000),
44+
45+
// Mixed leading/trailing characters
46+
"x*x",
47+
"x****************x",
48+
"x" + GLOB.repeat(1000000) + "x"
49+
)) {
50+
assertNotGlobMatch(pattern, "");
51+
}
52+
}
53+
54+
@Test
55+
public void testPatternWithoutGlobs() {
56+
assertGlobMatch("test", "test");
57+
}
58+
59+
@Test
60+
public void testGlobs() {
61+
for (String pattern : List.of(
62+
"*test", // Leading glob
63+
"this*", // Trailing glob
64+
"this*test", // Middle glob
65+
"*is *", // String in between two globs
66+
"*is*a*", // Lots of globs
67+
"**test**", // Double glob characters
68+
"**is**a***test*", // Varying number of globs
69+
"* *", // White space between globs
70+
"*", // Lone glob
71+
"**********", // Nothing but globs
72+
"*Ѿ*", // Unicode with globs
73+
"*is a ϗѾ *" // Mixed ASCII/unicode
74+
)) {
75+
assertGlobMatch(pattern, "this is a ϗѾ test");
76+
}
77+
78+
for(String pattern:List.of(
79+
"test*", // Implicit substring match
80+
"*is", // Partial match
81+
"*no*", // Globs without a match between them
82+
" ", // Plain white space
83+
"* ", // Trailing white space
84+
" *", // Leading white space
85+
"*ʤ*", // Non-matching unicode
86+
"this*this is a test" // Repeated prefix
87+
)) {
88+
assertNotGlobMatch(pattern, "this is a test");
89+
}
90+
}
91+
92+
public void assertGlobMatch(String pattern, String subject) {
93+
assertTrue(String.format("\"%s\" should match \"%s\"", pattern, subject), Glob.match(pattern, subject));
94+
}
95+
96+
public void assertNotGlobMatch(String pattern, String subject) {
97+
assertFalse(String.format("\"%s\" should not match \"%s\"", pattern, subject), Glob.match(pattern, subject));
98+
}
99+
100+
}

lib/src/test/java/io/cloudquery/helper/GlobMatcherTest.java

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)