package org.jabref.logic.util.io;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.jabref.model.entry.BibEntry;
import org.jabref.model.util.FileHelper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

class CiteKeyBasedFileFinder implements FileFinder {

    private static final Log LOGGER = LogFactory.getLog(CiteKeyBasedFileFinder.class);
    private final boolean exactKeyOnly;

    CiteKeyBasedFileFinder(boolean exactKeyOnly) {
        this.exactKeyOnly = exactKeyOnly;
    }

    @Override
    public Map<BibEntry, List<Path>> findAssociatedFiles(List<BibEntry> entries, List<Path> directories, List<String> extensions) {
        Objects.requireNonNull(directories);
        Objects.requireNonNull(entries);

        Map<BibEntry, List<Path>> result = new HashMap<>();

        // First scan directories
        Set<Path> filesWithExtension = findFilesByExtension(directories, extensions);

        // Initialize Result-Set
        for (BibEntry entry : entries) {
            result.put(entry, new ArrayList<>());
        }

        // Now look for keys
        nextFile:
        for (Path file : filesWithExtension) {
            String name = file.getFileName().toString();
            int dot = name.lastIndexOf('.');
            // First, look for exact matches:
            for (BibEntry entry : entries) {
                Optional<String> citeKey = entry.getCiteKeyOptional();
                if ((citeKey.isPresent()) && !citeKey.get().isEmpty() && (dot > 0)
                        && name.substring(0, dot).equals(citeKey.get())) {
                    result.get(entry).add(file);
                    continue nextFile;
                }
            }
            // If we get here, we did not find any exact matches. If non-exact
            // matches are allowed, try to find one:
            if (!exactKeyOnly) {
                for (BibEntry entry : entries) {
                    Optional<String> citeKey = entry.getCiteKeyOptional();
                    if ((citeKey.isPresent()) && !citeKey.get().isEmpty() && name.startsWith(citeKey.get())) {
                        result.get(entry).add(file);
                        continue nextFile;
                    }
                }
            }
        }

        return result;
    }

    /**
     * Returns a list of all files in the given directories which have one of the given extension.
     */
    public Set<Path> findFilesByExtension(List<Path> directories, List<String> extensions) {
        Objects.requireNonNull(extensions, "Extensions must not be null!");

        BiPredicate<Path, BasicFileAttributes> isFileWithCorrectExtension = (path, attributes) ->
                !Files.isDirectory(path)
                        && extensions.contains(FileHelper.getFileExtension(path).orElse(""));

        Set<Path> result = new HashSet<>();
        for (Path directory : directories) {
            try (Stream<Path> files = Files.find(directory, Integer.MAX_VALUE, isFileWithCorrectExtension)) {
                result.addAll(files.collect(Collectors.toSet()));
            } catch (IOException e) {
                LOGGER.error("Problem in finding files", e);
            }
        }
        return result;
    }
}

@@ -3298,32 +3298,79 @@
  directories) %7B%0A
+            if (Files.exists(directory)) %7B%0A    
             try 
@@ -3475,24 +3475,28 @@
             
+    
 result.addAl
@@ -3545,16 +3545,20 @@
         
+    
 %7D catch 
@@ -3591,16 +3591,20 @@
         
+   
+ 
 LOGGER.e
@@ -3636,24 +3636,42 @@
 files%22, e);%0A
+                %7D%0A
             

Fix "path not found" exception