-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Fix expansion of bracketed expressions in RegExpBasedFileFinder #7338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
95300c2
b7a394f
419c4d0
73410f8
d7ca1a0
81547bd
37d08ea
0a86a99
7c08423
062a285
4c2f2c3
c6ae6bd
6ce3e8a
beae66c
ccafaa2
c43875e
7af85e2
1a6826d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,17 +8,16 @@ | |
| import java.nio.file.Path; | ||
| import java.nio.file.attribute.BasicFileAttributes; | ||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.function.BiPredicate; | ||
| import java.util.function.Function; | ||
| import java.util.regex.Matcher; | ||
| import java.util.regex.Pattern; | ||
| import java.util.regex.PatternSyntaxException; | ||
| import java.util.stream.Collectors; | ||
| import java.util.stream.Stream; | ||
|
|
||
| import org.jabref.logic.citationkeypattern.BracketedPattern; | ||
| import org.jabref.model.database.BibDatabase; | ||
| import org.jabref.model.entry.BibEntry; | ||
| import org.jabref.model.strings.StringUtil; | ||
|
|
||
|
|
@@ -28,7 +27,6 @@ class RegExpBasedFileFinder implements FileFinder { | |
|
|
||
| private static final Pattern ESCAPE_PATTERN = Pattern.compile("([^\\\\])\\\\([^\\\\])"); | ||
|
|
||
| private static final Pattern SQUARE_BRACKETS_PATTERN = Pattern.compile("\\[.*?\\]"); | ||
| private final String regExp; | ||
| private final Character keywordDelimiter; | ||
|
|
||
|
|
@@ -41,21 +39,41 @@ class RegExpBasedFileFinder implements FileFinder { | |
| } | ||
|
|
||
| /** | ||
| * Takes a string that contains bracketed expression and expands each of these using getFieldAndFormat. | ||
| * <p> | ||
| * Unknown Bracket expressions are silently dropped. | ||
| * Creates a Pattern that matches the file name corresponding to the last element of {@code fileParts} with any bracketed patterns expanded. | ||
| * | ||
| * @throws IOException throws an IOException if a PatternSyntaxException occurs | ||
| */ | ||
| public static String expandBrackets(String bracketString, BibEntry entry, BibDatabase database, | ||
| Character keywordDelimiter) { | ||
| Matcher matcher = SQUARE_BRACKETS_PATTERN.matcher(bracketString); | ||
| StringBuilder expandedStringBuffer = new StringBuilder(); | ||
| while (matcher.find()) { | ||
| String replacement = BracketedPattern.expandBrackets(matcher.group(), keywordDelimiter, entry, database); | ||
| matcher.appendReplacement(expandedStringBuffer, replacement); | ||
| private Pattern createFileNamePattern(String[] fileParts, String extensionRegExp, BibEntry entry) throws IOException { | ||
| // Protect the extension marker so that it isn't treated as a bracketed pattern | ||
| String filePart = fileParts[fileParts.length - 1].replace("[extension]", EXT_MARKER); | ||
|
|
||
| // We need to supply a custom function to deal with the content of a bracketed expression and expandBracketContent is the default function | ||
| Function<String, String> expandBracket = BracketedPattern.expandBracketContent(keywordDelimiter, entry, null); | ||
| // but, we want to post-process the expanded content so that it can be used as a regex for finding a file name | ||
| Function<String, String> bracketToFileNameRegex = expandBracket.andThen(RegExpBasedFileFinder::toFileNameRegex); | ||
|
|
||
| String expandedBracketAsFileNameRegex = BracketedPattern.expandBrackets(filePart, bracketToFileNameRegex); | ||
|
|
||
| String fileNamePattern = expandedBracketAsFileNameRegex | ||
| .replaceAll(EXT_MARKER, extensionRegExp) // Replace the extension marker | ||
| .replaceAll("\\\\\\\\", "\\\\"); | ||
| try { | ||
| return Pattern.compile('^' + fileNamePattern + '$', Pattern.CASE_INSENSITIVE); | ||
| } catch (PatternSyntaxException e) { | ||
| throw new IOException(String.format("There is a syntax error in the regular expression %s used to search for files", fileNamePattern), e); | ||
| } | ||
| matcher.appendTail(expandedStringBuffer); | ||
| } | ||
|
|
||
| return expandedStringBuffer.toString(); | ||
| /** | ||
| * Helper method for both exact matching (if the file name were not created by JabRef) and cleaned file name matching. | ||
| * | ||
| * @param expandedContent the expanded content of a bracketed expression | ||
| * @return a String representation of a regex matching the expanded content and the expanded content cleaned for file name use | ||
| */ | ||
| private static String toFileNameRegex(String expandedContent) { | ||
| var cleanedContent = FileNameCleaner.cleanFileName(expandedContent); | ||
| return expandedContent.equals(cleanedContent) ? Pattern.quote(expandedContent) : | ||
| "(" + Pattern.quote(expandedContent) + ")|(" + Pattern.quote(cleanedContent) + ")"; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -142,9 +160,7 @@ private List<Path> findFile(final BibEntry entry, final Path directory, final St | |
| } | ||
|
|
||
| for (int index = 0; index < (fileParts.length - 1); index++) { | ||
|
|
||
| String dirToProcess = fileParts[index]; | ||
| dirToProcess = expandBrackets(dirToProcess, entry, null, keywordDelimiter); | ||
|
|
||
| if (dirToProcess.matches("^.:$")) { // Windows Drive Letter | ||
| actualDirectory = Path.of(dirToProcess + '/'); | ||
|
|
@@ -179,33 +195,21 @@ private List<Path> findFile(final BibEntry entry, final Path directory, final St | |
| resultFiles.addAll(findFile(entry, path, restOfFileString, extensionRegExp)); | ||
| } | ||
| } catch (UncheckedIOException ioe) { | ||
| throw new IOException(ioe); | ||
| throw ioe.getCause(); | ||
| } | ||
| } // End process directory information | ||
| } | ||
|
|
||
| // Last step: check if the given file can be found in this directory | ||
| String filePart = fileParts[fileParts.length - 1].replace("[extension]", EXT_MARKER); | ||
| String filenameToLookFor = expandBrackets(filePart, entry, null, keywordDelimiter).replaceAll(EXT_MARKER, extensionRegExp); | ||
|
|
||
| try { | ||
| final Pattern toMatch = Pattern.compile('^' + filenameToLookFor.replaceAll("\\\\\\\\", "\\\\") + '$', | ||
| Pattern.CASE_INSENSITIVE); | ||
| BiPredicate<Path, BasicFileAttributes> matcher = (path, attributes) -> toMatch.matcher(path.getFileName().toString()).matches(); | ||
| resultFiles.addAll(collectFilesWithMatcher(actualDirectory, matcher)); | ||
| } catch (UncheckedIOException | PatternSyntaxException e) { | ||
| throw new IOException("Could not look for " + filenameToLookFor, e); | ||
| } | ||
|
|
||
| return resultFiles; | ||
| } | ||
|
|
||
| private List<Path> collectFilesWithMatcher(Path actualDirectory, BiPredicate<Path, BasicFileAttributes> matcher) { | ||
| Pattern toMatch = createFileNamePattern(fileParts, extensionRegExp, entry); | ||
| BiPredicate<Path, BasicFileAttributes> matcher = (path, attributes) -> toMatch.matcher(path.getFileName().toString()).matches(); | ||
| try (Stream<Path> pathStream = Files.find(actualDirectory, 1, matcher, FileVisitOption.FOLLOW_LINKS)) { | ||
| return pathStream.collect(Collectors.toList()); | ||
| } catch (UncheckedIOException | IOException ioe) { | ||
| return Collections.emptyList(); | ||
| resultFiles.addAll(pathStream.collect(Collectors.toList())); | ||
| } catch (UncheckedIOException uncheckedIOException) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this even an UncheckedIOException? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Frankly, I am not sure why an |
||
| // Previously, an empty list were returned here on both IOException and UncheckedIOException | ||
| throw uncheckedIOException.getCause(); | ||
| } | ||
| return resultFiles; | ||
| } | ||
|
|
||
| private boolean isSubDirectory(Path rootDirectory, Path path) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.