package org.jabref.logic.cleanup;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.jabref.logic.layout.LayoutFormatterPreferences;
import org.jabref.logic.util.io.FileUtil;
import org.jabref.model.FieldChange;
import org.jabref.model.cleanup.CleanupJob;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.LinkedFile;
import org.jabref.model.metadata.FileDirectoryPreferences;
import org.jabref.model.util.FileHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class MoveFilesCleanup implements CleanupJob {
private static final Log LOGGER = LogFactory.getLog(MoveFilesCleanup.class);
private final BibDatabaseContext databaseContext;
private final FileDirectoryPreferences fileDirectoryPreferences;
private final LayoutFormatterPreferences layoutPrefs;
private final String fileDirPattern;
private LinkedFile singleFileFieldCleanup;
public MoveFilesCleanup(BibDatabaseContext databaseContext, String fileDirPattern,
FileDirectoryPreferences fileDirectoryPreferences, LayoutFormatterPreferences layoutPrefs) {
this.databaseContext = Objects.requireNonNull(databaseContext);
this.fileDirPattern = Objects.requireNonNull(fileDirPattern);
this.fileDirectoryPreferences = Objects.requireNonNull(fileDirectoryPreferences);
this.layoutPrefs = Objects.requireNonNull(layoutPrefs);
}
public MoveFilesCleanup(BibDatabaseContext databaseContext, String fileDirPattern,
FileDirectoryPreferences fileDirectoryPreferences, LayoutFormatterPreferences prefs,
LinkedFile field) {
this(databaseContext, fileDirPattern, fileDirectoryPreferences, prefs);
this.singleFileFieldCleanup = field;
}
@Override
public List<FieldChange> cleanup(BibEntry entry) {
Optional<Path> firstExistingFileDir = databaseContext.getFirstExistingFileDir(fileDirectoryPreferences);
if (!firstExistingFileDir.isPresent()) {
return Collections.emptyList();
}
List<Path> paths = databaseContext.getFileDirectoriesAsPaths(fileDirectoryPreferences);
String defaultFileDirectory = firstExistingFileDir.get().toString();
Optional<Path> targetDirectory = FileHelper.expandFilenameAsPath(defaultFileDirectory, paths);
if (!targetDirectory.isPresent()) {
return Collections.emptyList();
}
List<LinkedFile> fileList;
List<LinkedFile> newFileList;
if (singleFileFieldCleanup != null) {
fileList = Arrays.asList(singleFileFieldCleanup);
//Add all other except the current selected file
newFileList = entry.getFiles().stream().filter(name -> !name.equals(singleFileFieldCleanup))
.collect(Collectors.toList());
} else {
newFileList = new ArrayList<>();
fileList = entry.getFiles();
}
boolean changed = false;
for (LinkedFile fileEntry : fileList) {
String oldFileName = fileEntry.getLink();
Optional<Path> oldFile = fileEntry.findIn(paths);
if (!oldFile.isPresent() || !Files.exists(oldFile.get())) {
newFileList.add(fileEntry);
continue;
}
String targetDirName = "";
if (!fileDirPattern.isEmpty()) {
targetDirName = FileUtil.createFileNameFromPattern(databaseContext.getDatabase(), entry, fileDirPattern,
layoutPrefs);
}
Path newTargetFile = targetDirectory.get().resolve(targetDirName).resolve(oldFile.get().getFileName());
if (Files.exists(newTargetFile)) {
// We do not overwrite already existing files
newFileList.add(fileEntry);
continue;
}
try {
if (!Files.exists(newTargetFile)) {
Files.createDirectories(newTargetFile);
}
} catch (IOException e) {
LOGGER.error("Could no create necessary target directoires for renaming", e);
}
if (FileUtil.renameFile(oldFile.get(), newTargetFile, true)) {
changed = true;
String newEntryFilePath = Paths.get(defaultFileDirectory).relativize(newTargetFile).toString();
LinkedFile newFileEntry = fileEntry;
if (!oldFileName.equals(newTargetFile.toString())) {
newFileEntry = new LinkedFile(fileEntry.getDescription(), newEntryFilePath,
fileEntry.getFileType());
changed = true;
}
newFileList.add(newFileEntry);
}
}
if (changed) {
Optional<FieldChange> change = entry.setFiles(newFileList);
if (change.isPresent()) {
return Collections.singletonList(change.get());
} else {
return Collections.emptyList();
}
}
return Collections.emptyList();
}
}