diff --git a/src/main/java/net/sf/jabref/cli/ArgumentProcessor.java b/src/main/java/net/sf/jabref/cli/ArgumentProcessor.java index 43947823932..9a8b0d548bd 100644 --- a/src/main/java/net/sf/jabref/cli/ArgumentProcessor.java +++ b/src/main/java/net/sf/jabref/cli/ArgumentProcessor.java @@ -302,7 +302,7 @@ private boolean generateAux(List loaded, String[] data) { session.getEncoding().displayName()) + " " + session.getWriter().getProblemCharacters()); } - session.commit(new File(subName)); + session.commit(subName); } catch (SaveException ex) { System.err.println( Localization.lang("Could not save file.") + "\n" + ex.getLocalizedMessage()); @@ -344,7 +344,7 @@ private void exportFile(List loaded, String[] data) { session.getEncoding().displayName()) + " " + session.getWriter().getProblemCharacters()); } - session.commit(new File(data[0])); + session.commit(data[0]); } catch (SaveException ex) { System.err.println( Localization.lang("Could not save file.") + "\n" + ex.getLocalizedMessage()); diff --git a/src/main/java/net/sf/jabref/collab/ChangeScanner.java b/src/main/java/net/sf/jabref/collab/ChangeScanner.java index 3952e6627ec..7b45fa1c161 100644 --- a/src/main/java/net/sf/jabref/collab/ChangeScanner.java +++ b/src/main/java/net/sf/jabref/collab/ChangeScanner.java @@ -17,6 +17,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -97,8 +98,8 @@ public void run() { try { // Parse the temporary file. - File tempFile = Globals.fileUpdateMonitor.getTempFile(panel.fileMonitorHandle()); - ParserResult pr = OpenDatabaseAction.loadDatabase(tempFile, Globals.prefs.getDefaultEncoding()); + Path tempFile = Globals.fileUpdateMonitor.getTempFile(panel.fileMonitorHandle()); + ParserResult pr = OpenDatabaseAction.loadDatabase(tempFile.toFile(), Globals.prefs.getDefaultEncoding()); inTemp = pr.getDatabase(); mdInTemp = pr.getMetaData(); // Parse the modified file. diff --git a/src/main/java/net/sf/jabref/collab/FileUpdateMonitor.java b/src/main/java/net/sf/jabref/collab/FileUpdateMonitor.java index 80d0df9ba14..2a648371192 100644 --- a/src/main/java/net/sf/jabref/collab/FileUpdateMonitor.java +++ b/src/main/java/net/sf/jabref/collab/FileUpdateMonitor.java @@ -17,6 +17,8 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashMap; import java.util.Map; @@ -133,7 +135,7 @@ public void updateTimeStamp(String key) { * @throws IllegalArgumentException If the handle doesn't correspond to an entry. * @return File The temporary file. */ - public File getTempFile(String key) throws IllegalArgumentException { + public Path getTempFile(String key) throws IllegalArgumentException { Object o = entries.get(key); if (o == null) { throw new IllegalArgumentException("Entry not found"); @@ -149,7 +151,7 @@ static class Entry { private final FileUpdateListener listener; private final File file; - private final File tmpFile; + private final Path tmpFile; private long timeStamp; private long fileSize; @@ -161,7 +163,7 @@ public Entry(FileUpdateListener ul, File f) { fileSize = file.length(); tmpFile = FileUpdateMonitor.getTempFile(); if (tmpFile != null) { - tmpFile.deleteOnExit(); + tmpFile.toFile().deleteOnExit(); copy(); } } @@ -194,9 +196,9 @@ public boolean copy() { boolean res = false; try { - res = FileUtil.copyFile(file, tmpFile, true); + res = FileUtil.copyFile(file, tmpFile.toFile(), true); } catch (IOException ex) { - LOGGER.info("Cannot copy to temporary file '" + tmpFile.getPath() + '\'', ex); + LOGGER.info("Cannot copy to temporary file '" + tmpFile + '\'', ex); } return res; } @@ -218,7 +220,7 @@ public void notifyFileRemoved() { listener.fileRemoved(); } - public File getTmpFile() { + public Path getTmpFile() { return tmpFile; } @@ -228,14 +230,14 @@ public void decreaseTimeStamp() { } - private static synchronized File getTempFile() { - File f = null; + private static synchronized Path getTempFile() { + Path temporaryFile = null; try { - f = File.createTempFile("jabref", null); - f.deleteOnExit(); + temporaryFile = Files.createTempFile("jabref", null); + temporaryFile.toFile().deleteOnExit(); } catch (IOException ex) { LOGGER.warn("Could not create temporary file.", ex); } - return f; + return temporaryFile; } } diff --git a/src/main/java/net/sf/jabref/exporter/AutoSaveManager.java b/src/main/java/net/sf/jabref/exporter/AutoSaveManager.java index 05119a4927c..6e62c22d6da 100644 --- a/src/main/java/net/sf/jabref/exporter/AutoSaveManager.java +++ b/src/main/java/net/sf/jabref/exporter/AutoSaveManager.java @@ -98,7 +98,7 @@ private static boolean autoSave(BasePanel panel) { BibDatabaseWriter databaseWriter = new BibtexDatabaseWriter(FileSaveSession::new); SaveSession ss = databaseWriter.saveDatabase(panel.getBibDatabaseContext(), prefs); - ss.commit(backupFile); + ss.commit(backupFile.toPath()); } catch (SaveException e) { LOGGER.error("Problem with automatic save", e); return false; diff --git a/src/main/java/net/sf/jabref/exporter/BibDatabaseWriter.java b/src/main/java/net/sf/jabref/exporter/BibDatabaseWriter.java index b8270ddb945..198b4a518c9 100644 --- a/src/main/java/net/sf/jabref/exporter/BibDatabaseWriter.java +++ b/src/main/java/net/sf/jabref/exporter/BibDatabaseWriter.java @@ -229,9 +229,7 @@ public E savePartOfDatabase(BibDatabaseContext bibDatabaseContext, * Writes all data to the specified writer, using each object's toString() method. */ protected void writeMetaData(MetaData metaData) throws SaveException { - if (metaData == null) { - return; - } + Objects.requireNonNull(metaData); Map serializedMetaData = metaData.getAsStringMap(); @@ -288,10 +286,12 @@ protected void writeString(BibtexString bibtexString, boolean isFirstString, Map String foundLabel = m.group(1); int restIndex = content.indexOf(foundLabel) + foundLabel.length(); content = content.substring(restIndex); - Object referred = remaining.get(foundLabel.substring(1, foundLabel.length() - 1)); + String label = foundLabel.substring(1, foundLabel.length() - 1); + // If the label we found exists as a key in the "remaining" Map, we go on and write it now: - if (referred != null) { - writeString((BibtexString) referred, isFirstString, remaining, maxKeyLength, reformatFile); + if (remaining.containsKey(label)) { + BibtexString referred = remaining.get(label); + writeString(referred, isFirstString, remaining, maxKeyLength, reformatFile); } } diff --git a/src/main/java/net/sf/jabref/exporter/ExportFormat.java b/src/main/java/net/sf/jabref/exporter/ExportFormat.java index a3a315345c8..8b2eb436f6f 100644 --- a/src/main/java/net/sf/jabref/exporter/ExportFormat.java +++ b/src/main/java/net/sf/jabref/exporter/ExportFormat.java @@ -24,6 +24,7 @@ import java.net.URL; import java.nio.charset.Charset; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -186,7 +187,7 @@ public void performExport(final BibDatabaseContext databaseContext, final String if (entries.isEmpty()) { // Do not export if no entries to export -- avoids exports with only template text return; } - File outFile = new File(file); + Path outFile = Paths.get(file); SaveSession ss = null; if (this.encoding != null) { try { @@ -369,7 +370,7 @@ public FileFilter getFileFilter() { return fileFilter; } - public void finalizeSaveSession(final SaveSession ss, File file) throws SaveException, IOException { + public void finalizeSaveSession(final SaveSession ss, Path file) throws SaveException, IOException { ss.getWriter().flush(); ss.getWriter().close(); diff --git a/src/main/java/net/sf/jabref/exporter/FileSaveSession.java b/src/main/java/net/sf/jabref/exporter/FileSaveSession.java index 194ade95493..9530b6fbe04 100644 --- a/src/main/java/net/sf/jabref/exporter/FileSaveSession.java +++ b/src/main/java/net/sf/jabref/exporter/FileSaveSession.java @@ -15,7 +15,6 @@ */ package net.sf.jabref.exporter; -import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -84,16 +83,15 @@ private static Path createTemporaryFile() throws SaveException { } @Override - public void commit(File file) throws SaveException { + public void commit(Path file) throws SaveException { if (file == null) { return; } - if (file.exists() && backup) { - String name = file.getName(); - String path = file.getParent(); - File backupFile = new File(path, name + BACKUP_EXTENSION); + if (backup && Files.exists(file)) { + Path fileName = file.getFileName(); + Path backupFile = file.resolveSibling(fileName + BACKUP_EXTENSION); try { - FileUtil.copyFile(file, backupFile, true); + FileUtil.copyFile(file.toFile(), backupFile.toFile(), true); } catch (IOException ex) { LOGGER.error("Problem copying file", ex); throw SaveException.BACKUP_CREATION; @@ -113,7 +111,7 @@ public void commit(File file) throws SaveException { } } - FileUtil.copyFile(temporaryFile.toFile(), file, true); + FileUtil.copyFile(temporaryFile.toFile(), file.toFile(), true); } catch (IOException ex2) { // If something happens here, what can we do to correct the problem? The file is corrupted, but we still // have a clean copy in tmp. However, we just failed to copy tmp to file, so it's not likely that diff --git a/src/main/java/net/sf/jabref/exporter/MSBibExportFormat.java b/src/main/java/net/sf/jabref/exporter/MSBibExportFormat.java index c4a3f679b38..f33852aec1b 100644 --- a/src/main/java/net/sf/jabref/exporter/MSBibExportFormat.java +++ b/src/main/java/net/sf/jabref/exporter/MSBibExportFormat.java @@ -15,10 +15,10 @@ */ package net.sf.jabref.exporter; -import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; import java.util.List; import java.util.Objects; @@ -66,7 +66,7 @@ public void performExport(final BibDatabaseContext databaseContext, final String } catch (TransformerException | IllegalArgumentException | TransformerFactoryConfigurationError e) { throw new Error(e); } - finalizeSaveSession(session, new File(file)); + finalizeSaveSession(session, Paths.get(file)); } catch (IOException ex) { throw new SaveException(ex); } diff --git a/src/main/java/net/sf/jabref/exporter/ModsExportFormat.java b/src/main/java/net/sf/jabref/exporter/ModsExportFormat.java index a82c9092974..b43f079a622 100644 --- a/src/main/java/net/sf/jabref/exporter/ModsExportFormat.java +++ b/src/main/java/net/sf/jabref/exporter/ModsExportFormat.java @@ -15,10 +15,10 @@ */ package net.sf.jabref.exporter; -import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; import java.util.List; import java.util.Objects; @@ -65,7 +65,7 @@ public void performExport(final BibDatabaseContext databaseContext, final String } catch (TransformerException | IllegalArgumentException | TransformerFactoryConfigurationError e) { throw new Error(e); } - finalizeSaveSession(ss, new File(file)); + finalizeSaveSession(ss, Paths.get(file)); } catch (IOException ex) { throw new SaveException(ex); } diff --git a/src/main/java/net/sf/jabref/exporter/SaveDatabaseAction.java b/src/main/java/net/sf/jabref/exporter/SaveDatabaseAction.java index ac5cfadf321..a5bc59ad853 100644 --- a/src/main/java/net/sf/jabref/exporter/SaveDatabaseAction.java +++ b/src/main/java/net/sf/jabref/exporter/SaveDatabaseAction.java @@ -120,7 +120,7 @@ public void run() { // lacking keys, before saving: panel.autoGenerateKeysBeforeSaving(); - if (FileBasedLock.waitForFileLock(panel.getBibDatabaseContext().getDatabaseFile(), 10)) { + if (FileBasedLock.waitForFileLock(panel.getBibDatabaseContext().getDatabaseFile().toPath(), 10)) { // Check for external modifications to alleviate multiuser concurrency issue when near // simultaneous saves occur to a shared database file: if true, do not perform the save // rather return instead. @@ -244,7 +244,7 @@ private boolean saveDatabase(File file, boolean selectedOnly, Charset encoding) try { if (commit) { - session.commit(file); + session.commit(file.toPath()); panel.getBibDatabaseContext().getMetaData().setEncoding(encoding); // Make sure to remember which encoding we used. } else { session.cancel(); @@ -256,7 +256,7 @@ private boolean saveDatabase(File file, boolean selectedOnly, Charset encoding) JOptionPane.YES_NO_OPTION); if (ans == JOptionPane.YES_OPTION) { session.setUseBackup(false); - session.commit(file); + session.commit(file.toPath()); panel.getBibDatabaseContext().getMetaData().setEncoding(encoding); } else { commit = false; @@ -383,7 +383,7 @@ private boolean checkExternalModification() { JabRefExecutorService.INSTANCE.execute(() -> { - if (!FileBasedLock.waitForFileLock(panel.getBibDatabaseContext().getDatabaseFile(), 10)) { + if (!FileBasedLock.waitForFileLock(panel.getBibDatabaseContext().getDatabaseFile().toPath(), 10)) { // TODO: GUI handling of the situation when the externally modified file keeps being locked. LOGGER.error("File locked, this will be trouble."); } diff --git a/src/main/java/net/sf/jabref/exporter/SaveSession.java b/src/main/java/net/sf/jabref/exporter/SaveSession.java index 3f2cca1cb4b..b7fb7e097b3 100644 --- a/src/main/java/net/sf/jabref/exporter/SaveSession.java +++ b/src/main/java/net/sf/jabref/exporter/SaveSession.java @@ -17,8 +17,9 @@ package net.sf.jabref.exporter; -import java.io.File; import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -50,7 +51,11 @@ public void setUseBackup(boolean useBackup) { this.backup = useBackup; } - public abstract void commit(File file) throws SaveException; + public abstract void commit(Path file) throws SaveException; + + public void commit(String path) throws SaveException { + commit(Paths.get(path)); + } public abstract void cancel(); diff --git a/src/main/java/net/sf/jabref/exporter/StringSaveSession.java b/src/main/java/net/sf/jabref/exporter/StringSaveSession.java index f44e51692d3..3fec5c30f30 100644 --- a/src/main/java/net/sf/jabref/exporter/StringSaveSession.java +++ b/src/main/java/net/sf/jabref/exporter/StringSaveSession.java @@ -18,11 +18,11 @@ package net.sf.jabref.exporter; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.file.Files; +import java.nio.file.Path; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -52,9 +52,9 @@ public String getStringValue() { } @Override - public void commit(File file) throws SaveException { + public void commit(Path file) throws SaveException { try { - Files.write(file.toPath(), outputStream.toByteArray()); + Files.write(file, outputStream.toByteArray()); } catch (IOException e) { throw new SaveException(e); } diff --git a/src/main/java/net/sf/jabref/gui/BasePanel.java b/src/main/java/net/sf/jabref/gui/BasePanel.java index 10313f8b01b..32ec8578832 100644 --- a/src/main/java/net/sf/jabref/gui/BasePanel.java +++ b/src/main/java/net/sf/jabref/gui/BasePanel.java @@ -1176,7 +1176,7 @@ private boolean saveDatabase(File file, boolean selectedOnly, Charset enc, } if (commit) { - session.commit(file); + session.commit(file.toPath()); this.bibDatabaseContext.getMetaData().setEncoding(enc); // Make sure to remember which encoding we used. } else { session.cancel(); @@ -2177,7 +2177,7 @@ public void fileUpdated() { // Test: running scan automatically in background if ((getBibDatabaseContext().getDatabaseFile() != null) - && !FileBasedLock.waitForFileLock(getBibDatabaseContext().getDatabaseFile(), 10)) { + && !FileBasedLock.waitForFileLock(getBibDatabaseContext().getDatabaseFile().toPath(), 10)) { // The file is locked even after the maximum wait. Do nothing. LOGGER.error("File updated externally, but change scan failed because the file is locked."); // Perturb the stored timestamp so successive checks are made: diff --git a/src/main/java/net/sf/jabref/importer/OpenDatabaseAction.java b/src/main/java/net/sf/jabref/importer/OpenDatabaseAction.java index 0281e45946d..4777284a30e 100644 --- a/src/main/java/net/sf/jabref/importer/OpenDatabaseAction.java +++ b/src/main/java/net/sf/jabref/importer/OpenDatabaseAction.java @@ -19,10 +19,12 @@ import java.io.File; import java.io.IOException; import java.nio.charset.Charset; +import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Optional; import javax.swing.Action; import javax.swing.JOptionPane; @@ -216,10 +218,11 @@ private void openTheFile(File file, boolean raisePanel) { Globals.prefs.put(JabRefPreferences.WORKING_DIRECTORY, file.getPath()); // Should this be done _after_ we know it was successfully opened? - if (FileBasedLock.hasLockFile(file)) { - long modificationTIme = FileBasedLock.getLockFileTimeStamp(file); - if ((modificationTIme != -1) - && ((System.currentTimeMillis() - modificationTIme) > FileBasedLock.LOCKFILE_CRITICAL_AGE)) { + if (FileBasedLock.hasLockFile(file.toPath())) { + Optional modificationTime = FileBasedLock.getLockFileTimeStamp(file.toPath()); + if ((modificationTime.isPresent()) && ( + (System.currentTimeMillis() - modificationTime.get().toMillis()) + > FileBasedLock.LOCKFILE_CRITICAL_AGE)) { // The lock file is fairly old, so we can offer to "steal" the file: int answer = JOptionPane.showConfirmDialog(null, "" + Localization.lang("Error opening file") + " '" + fileName + "'. " @@ -227,11 +230,11 @@ private void openTheFile(File file, boolean raisePanel) { + Localization.lang("Do you want to override the file lock?"), Localization.lang("File locked"), JOptionPane.YES_NO_OPTION); if (answer == JOptionPane.YES_OPTION) { - FileBasedLock.deleteLockFile(file); + FileBasedLock.deleteLockFile(file.toPath()); } else { return; } - } else if (!FileBasedLock.waitForFileLock(file, 10)) { + } else if (!FileBasedLock.waitForFileLock(file.toPath(), 10)) { JOptionPane.showMessageDialog(null, Localization.lang("Error opening file") + " '" + fileName + "'. " + Localization.lang("File is locked by another JabRef instance."), @@ -382,7 +385,7 @@ public static ParserResult loadDatabaseOrAutoSave(String name, boolean ignoreAut } } - if (!FileBasedLock.waitForFileLock(file, 10)) { + if (!FileBasedLock.waitForFileLock(file.toPath(), 10)) { LOGGER.error(Localization.lang("Error opening file") + " '" + name + "'. " + "File is locked by another JabRef instance."); return ParserResult.getNullResult(); diff --git a/src/main/java/net/sf/jabref/logic/util/io/FileBasedLock.java b/src/main/java/net/sf/jabref/logic/util/io/FileBasedLock.java index 6cdbd69d282..2fff8d7e83d 100644 --- a/src/main/java/net/sf/jabref/logic/util/io/FileBasedLock.java +++ b/src/main/java/net/sf/jabref/logic/util/io/FileBasedLock.java @@ -1,8 +1,11 @@ package net.sf.jabref.logic.util.io; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.util.Optional; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -26,7 +29,7 @@ public class FileBasedLock { * @param maxWaitCount The maximum number of times to wait. * @return true if the lock file is gone, false if it is still there. */ - public static boolean waitForFileLock(File file, int maxWaitCount) { + public static boolean waitForFileLock(Path file, int maxWaitCount) { // Check if the file is locked by another JabRef user: int lockCheckCount = 0; while (hasLockFile(file)) { @@ -48,19 +51,25 @@ public static boolean waitForFileLock(File file, int maxWaitCount) { * @param file The file to check. * @return true if a lock file exists, false otherwise. */ - public static boolean hasLockFile(File file) { - File lock = new File(file.getPath() + LOCKFILE_SUFFIX); - return lock.exists(); + public static boolean hasLockFile(Path file) { + Path lockFile = getLockFilePath(file); + return Files.exists(lockFile); } /** * Find the lock file's last modified time, if it has a lock file. * @param file The file to check. - * @return the last modified time if lock file exists, -1 otherwise. + * @return the last modified time if lock file exists, empty optional otherwise. */ - public static long getLockFileTimeStamp(File file) { - File lock = new File(file.getPath() + LOCKFILE_SUFFIX); - return lock.exists() ? lock.lastModified() : -1; + public static Optional getLockFileTimeStamp(Path file) { + Path lockFile = getLockFilePath(file); + try { + return Files.exists(lockFile) ? + Optional.of(Files.readAttributes(lockFile, BasicFileAttributes.class).lastModifiedTime()) : + Optional.empty(); + } catch (IOException e) { + return Optional.empty(); + } } /** @@ -68,13 +77,15 @@ public static long getLockFileTimeStamp(File file) { * * @return true if the lock file existed, false otherwise. */ - public static boolean deleteLockFile(File file) { - File lock = new File(file.getPath() + LOCKFILE_SUFFIX); - if (!lock.exists()) { + public static boolean deleteLockFile(Path file) { + Path lockFile = getLockFilePath(file); + if (!Files.exists(lockFile)) { return false; } - if (!lock.delete()) { - LOGGER.warn("Cannot delete lock file"); + try { + Files.delete(lockFile); + } catch (IOException e) { + LOGGER.warn("Cannot delete lock file", e); } return true; } @@ -85,18 +96,22 @@ public static boolean deleteLockFile(File file) { * @return true if the lock file already existed * @throws IOException if something happens during creation. */ - public static boolean createLockFile(File file) throws IOException { - File lock = new File(file.getPath() + LOCKFILE_SUFFIX); - if (lock.exists()) { + public static boolean createLockFile(Path file) throws IOException { + Path lockFile = getLockFilePath(file); + if (Files.exists(lockFile)) { return true; } - try (FileOutputStream out = new FileOutputStream(lock)) { - out.write(0); - out.close(); + + try { + Files.write(lockFile, "0".getBytes()); } catch (IOException ex) { LOGGER.error("Error when creating lock file.", ex); } - lock.deleteOnExit(); + lockFile.toFile().deleteOnExit(); return false; } + + private static Path getLockFilePath(Path file) { + return file.resolveSibling(file.getFileName() + LOCKFILE_SUFFIX); + } }