Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ public class FileSystems {
private static final Pattern FILE_SCHEME_PATTERN =
Pattern.compile("(?<scheme>[a-zA-Z][-a-zA-Z0-9+.]*):/.*");
private static final Pattern GLOB_PATTERN = Pattern.compile("[*?{}]");
private static final Pattern ESCAPED_GLOB_PATTERN = Pattern.compile("\\\\[*?{}]");
private static final String GLOB_ESCAPE_PREFIX = "\\";

private static final AtomicReference<KV<Long, Integer>> FILESYSTEM_REVISION =
new AtomicReference<>();
Expand All @@ -92,6 +94,40 @@ public static boolean hasGlobWildcard(String spec) {
return GLOB_PATTERN.matcher(spec).find();
}

/**
* Escapes glob wildcard characters in the given spec so they are treated as literals.
*
* <p>This method escapes the characters '*', '?', '{', and '}' by prefixing them with a
* backslash, allowing them to be treated as literal characters in a file path rather than as
* glob wildcards.
*
* <p>Example: {@code escapeGlobWildcards("file*.txt")} returns {@code "file\\*.txt"}
*
* @param spec the file path specification to escape
* @return the escaped specification
*/
public static String escapeGlobWildcards(String spec) {
checkNotNull(spec, "spec cannot be null");
return spec.replaceAll("([*?{}])", "\\\\$1");
}

/**
* Unescapes glob wildcard characters in the given spec that were previously escaped with {@link
* #escapeGlobWildcards(String)}.
*
* <p>This method removes the backslash prefix from escaped glob characters ('*', '?', '{', '}'),
* restoring them to their unescaped form.
*
* <p>Example: {@code unescapeGlobWildcards("file\\*.txt")} returns {@code "file*.txt"}
*
* @param spec the file path specification to unescape
* @return the unescaped specification
*/
public static String unescapeGlobWildcards(String spec) {
checkNotNull(spec, "spec cannot be null");
return spec.replaceAll("\\\\([*?{}])", "$1");
}

/**
* This is the entry point to convert user-provided specs to {@link ResourceId ResourceIds}.
* Callers should use {@link #match} to resolve users specs ambiguities before calling other
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,56 @@ private void createFileWithContent(Path path, String content) throws Exception {
}
}

@Test
public void testEscapeGlobWildcards() {
// Test escaping asterisk
assertEquals("file\\*.txt", FileSystems.escapeGlobWildcards("file*.txt"));

// Test escaping question mark
assertEquals("file\\?.txt", FileSystems.escapeGlobWildcards("file?.txt"));

// Test escaping braces
assertEquals("file\\{1,2\\}.txt", FileSystems.escapeGlobWildcards("file{1,2}.txt"));

// Test escaping multiple characters
assertEquals("\\*\\?\\{\\}.txt", FileSystems.escapeGlobWildcards("*?{}.txt"));

// Test string with no glob characters
assertEquals("file.txt", FileSystems.escapeGlobWildcards("file.txt"));

// Test empty string
assertEquals("", FileSystems.escapeGlobWildcards(""));
}

@Test
public void testUnescapeGlobWildcards() {
// Test unescaping asterisk
assertEquals("file*.txt", FileSystems.unescapeGlobWildcards("file\\*.txt"));

// Test unescaping question mark
assertEquals("file?.txt", FileSystems.unescapeGlobWildcards("file\\?.txt"));

// Test unescaping braces
assertEquals("file{1,2}.txt", FileSystems.unescapeGlobWildcards("file\\{1,2\\}.txt"));

// Test unescaping multiple characters
assertEquals("*?{}.txt", FileSystems.unescapeGlobWildcards("\\*\\?\\{\\}.txt"));

// Test string with no escaped characters
assertEquals("file.txt", FileSystems.unescapeGlobWildcards("file.txt"));

// Test empty string
assertEquals("", FileSystems.unescapeGlobWildcards(""));
}

@Test
public void testEscapeUnescapeRoundTrip() {
String original = "file*test?.txt";
String escaped = FileSystems.escapeGlobWildcards(original);
String unescaped = FileSystems.unescapeGlobWildcards(escaped);
assertEquals(original, unescaped);
}

private LocalResourceId toLocalResourceId(String str) throws Exception {
boolean isDirectory;
if (SystemUtils.IS_OS_WINDOWS) {
Expand Down
Loading