diff --git a/src/main/java/org/jabref/architecture/AllowedToUseClassGetResource.java b/src/main/java/org/jabref/architecture/AllowedToUseClassGetResource.java
new file mode 100644
index 00000000000..a8f98bdc549
--- /dev/null
+++ b/src/main/java/org/jabref/architecture/AllowedToUseClassGetResource.java
@@ -0,0 +1,11 @@
+package org.jabref.architecture;
+
+/**
+ * Annotation to indicate that this logic class can use class.getResource().
+ * Mostly, because {@link java.nio.file.Path} is not used.
+ * See https://github.com/oracle/graal/issues/7682 for a longer discussion.
+ */
+public @interface AllowedToUseClassGetResource {
+ // The rationale
+ String value();
+}
diff --git a/src/main/java/org/jabref/architecture/AllowedToUseSwing.java b/src/main/java/org/jabref/architecture/AllowedToUseSwing.java
index 99a40a4bade..6820421c880 100644
--- a/src/main/java/org/jabref/architecture/AllowedToUseSwing.java
+++ b/src/main/java/org/jabref/architecture/AllowedToUseSwing.java
@@ -4,7 +4,6 @@
* Annotation to indicate that this logic class can access swing
*/
public @interface AllowedToUseSwing {
-
// The rationale
String value();
}
diff --git a/src/main/java/org/jabref/gui/fieldeditors/journalinfo/JournalInfoView.java b/src/main/java/org/jabref/gui/fieldeditors/journalinfo/JournalInfoView.java
index adffd08e287..24712ff4a3b 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/journalinfo/JournalInfoView.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/journalinfo/JournalInfoView.java
@@ -8,10 +8,12 @@
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
+import org.jabref.architecture.AllowedToUseClassGetResource;
import org.jabref.logic.importer.FetcherException;
import com.airhacks.afterburner.views.ViewLoader;
+@AllowedToUseClassGetResource("JavaFX internally handles the passed URLs properly.")
public class JournalInfoView extends VBox {
@FXML private Label title;
@FXML private Label categories;
diff --git a/src/main/java/org/jabref/gui/groups/GroupTreeView.java b/src/main/java/org/jabref/gui/groups/GroupTreeView.java
index 8bae9b4353c..ef837d2586e 100644
--- a/src/main/java/org/jabref/gui/groups/GroupTreeView.java
+++ b/src/main/java/org/jabref/gui/groups/GroupTreeView.java
@@ -43,6 +43,7 @@
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
+import org.jabref.architecture.AllowedToUseClassGetResource;
import org.jabref.gui.DialogService;
import org.jabref.gui.DragAndDropDataFormats;
import org.jabref.gui.StateManager;
@@ -70,6 +71,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+@AllowedToUseClassGetResource("JavaFX internally handles the passed URLs properly.")
public class GroupTreeView extends BorderPane {
private static final Logger LOGGER = LoggerFactory.getLogger(GroupTreeView.class);
diff --git a/src/main/java/org/jabref/gui/icon/IconTheme.java b/src/main/java/org/jabref/gui/icon/IconTheme.java
index cc9bad0528b..857aee790d5 100644
--- a/src/main/java/org/jabref/gui/icon/IconTheme.java
+++ b/src/main/java/org/jabref/gui/icon/IconTheme.java
@@ -22,6 +22,8 @@
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
+import org.jabref.architecture.AllowedToUseClassGetResource;
+
import org.kordamp.ikonli.Ikon;
import org.kordamp.ikonli.IkonProvider;
import org.kordamp.ikonli.materialdesign2.MaterialDesignA;
@@ -50,6 +52,7 @@
import static java.util.EnumSet.allOf;
+@AllowedToUseClassGetResource("JavaFX internally handles the passed URLs properly.")
public class IconTheme {
public static final Color DEFAULT_DISABLED_COLOR = Color.web("#c8c8c8");
diff --git a/src/main/java/org/jabref/gui/icon/JabRefIkonHandler.java b/src/main/java/org/jabref/gui/icon/JabRefIkonHandler.java
index 2f9bc45e0d1..b09703d9189 100644
--- a/src/main/java/org/jabref/gui/icon/JabRefIkonHandler.java
+++ b/src/main/java/org/jabref/gui/icon/JabRefIkonHandler.java
@@ -3,9 +3,12 @@
import java.io.InputStream;
import java.net.URL;
+import org.jabref.architecture.AllowedToUseClassGetResource;
+
import org.kordamp.ikonli.AbstractIkonHandler;
import org.kordamp.ikonli.Ikon;
+@AllowedToUseClassGetResource("JavaFX internally handles the passed URLs properly.")
public class JabRefIkonHandler extends AbstractIkonHandler {
private static String FONT_RESOURCE = "/fonts/JabRefMaterialDesign.ttf";
diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java
index 8ad0bd8fc43..16b51d631cc 100644
--- a/src/main/java/org/jabref/gui/maintable/MainTable.java
+++ b/src/main/java/org/jabref/gui/maintable/MainTable.java
@@ -24,6 +24,7 @@
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
+import org.jabref.architecture.AllowedToUseClassGetResource;
import org.jabref.gui.ClipBoardManager;
import org.jabref.gui.DialogService;
import org.jabref.gui.DragAndDropDataFormats;
@@ -60,6 +61,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+@AllowedToUseClassGetResource("JavaFX internally handles the passed URLs properly.")
public class MainTable extends TableView {
private static final Logger LOGGER = LoggerFactory.getLogger(MainTable.class);
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java
index ac04d895b11..131b8477145 100644
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java
@@ -11,6 +11,7 @@
import javafx.scene.layout.VBox;
import javafx.stage.Screen;
+import org.jabref.architecture.AllowedToUseClassGetResource;
import org.jabref.gui.mergeentries.newmergedialog.fieldsmerger.FieldMergerFactory;
import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar;
import org.jabref.logic.l10n.Localization;
@@ -19,6 +20,7 @@
import org.jabref.model.entry.field.FieldProperty;
import org.jabref.preferences.PreferencesService;
+@AllowedToUseClassGetResource("JavaFX internally handles the passed URLs properly.")
public class ThreeWayMergeView extends VBox {
public static final int GRID_COLUMN_MIN_WIDTH = 250;
diff --git a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java
index 8d44dc3e492..5e26e14699a 100644
--- a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java
+++ b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java
@@ -40,6 +40,7 @@
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
+import org.jabref.architecture.AllowedToUseClassGetResource;
import org.jabref.gui.ClipBoardManager;
import org.jabref.gui.DialogService;
import org.jabref.gui.LibraryTabContainer;
@@ -445,6 +446,7 @@ public void setSearchTerm(String searchTerm) {
UiTaskExecutor.runInJavaFXThread(() -> searchField.setText(searchTerm));
}
+ @AllowedToUseClassGetResource("JavaFX internally handles the passed URLs properly.")
private static class SearchPopupSkin implements Skin> {
private final AutoCompletePopup control;
diff --git a/src/main/java/org/jabref/gui/search/SearchResultsTable.java b/src/main/java/org/jabref/gui/search/SearchResultsTable.java
index 935a996642e..b74e80d9109 100644
--- a/src/main/java/org/jabref/gui/search/SearchResultsTable.java
+++ b/src/main/java/org/jabref/gui/search/SearchResultsTable.java
@@ -8,6 +8,7 @@
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
+import org.jabref.architecture.AllowedToUseClassGetResource;
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.maintable.BibEntryTableViewModel;
@@ -22,6 +23,7 @@
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.preferences.PreferencesService;
+@AllowedToUseClassGetResource("JavaFX internally handles the passed URLs properly.")
public class SearchResultsTable extends TableView {
public SearchResultsTable(SearchResultsTableDataModel model,
diff --git a/src/main/java/org/jabref/gui/theme/StyleSheet.java b/src/main/java/org/jabref/gui/theme/StyleSheet.java
index 19709f013ce..63db7bcacf1 100644
--- a/src/main/java/org/jabref/gui/theme/StyleSheet.java
+++ b/src/main/java/org/jabref/gui/theme/StyleSheet.java
@@ -9,11 +9,13 @@
import java.nio.file.Path;
import java.util.Optional;
+import org.jabref.architecture.AllowedToUseClassGetResource;
import org.jabref.gui.JabRefGUI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+@AllowedToUseClassGetResource("JavaFX internally handles the passed URLs properly.")
abstract class StyleSheet {
static final String DATA_URL_PREFIX = "data:text/css;charset=utf-8;base64,";
diff --git a/src/main/java/org/jabref/gui/theme/StyleSheetFile.java b/src/main/java/org/jabref/gui/theme/StyleSheetFile.java
index 3e98abf7142..0fb16e363c4 100644
--- a/src/main/java/org/jabref/gui/theme/StyleSheetFile.java
+++ b/src/main/java/org/jabref/gui/theme/StyleSheetFile.java
@@ -87,7 +87,7 @@ public URL getSceneStylesheet() {
* This method allows callers to obtain the theme's additional stylesheet.
*
* @return the stylesheet location if there is an additional stylesheet present and available. The
- * location will be a local URL. Typically it will be a {@code 'data:'} URL where the CSS is embedded. However for
+ * location will be a local URL. Typically, it will be a {@code 'data:'} URL where the CSS is embedded. However, for
* large themes it can be {@code 'file:'}.
*/
@Override
diff --git a/src/main/java/org/jabref/logic/citationstyle/CitationStyle.java b/src/main/java/org/jabref/logic/citationstyle/CitationStyle.java
index 0ebd516e5b2..5b042e941ad 100644
--- a/src/main/java/org/jabref/logic/citationstyle/CitationStyle.java
+++ b/src/main/java/org/jabref/logic/citationstyle/CitationStyle.java
@@ -1,11 +1,11 @@
package org.jabref.logic.citationstyle;
import java.io.IOException;
+import java.io.InputStream;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
@@ -20,9 +20,9 @@
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
+import org.jabref.architecture.AllowedToUseClassGetResource;
import org.jabref.logic.util.StandardFileType;
-import de.undercouch.citeproc.helper.CSLUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.CharacterData;
@@ -35,6 +35,7 @@
/**
* Representation of a CitationStyle. Stores its name, the file path and the style itself
*/
+@AllowedToUseClassGetResource("org.jabref.logic.citationstyle.CitationStyle.discoverCitationStyles reads the whole path to discover all available styles. Should be converted to a build-time job.")
public class CitationStyle {
public static final String DEFAULT = "/ieee.csl";
@@ -57,31 +58,41 @@ private CitationStyle(final String filename, final String title, final String so
/**
* Creates an CitationStyle instance out of the style string
*/
- private static Optional createCitationStyleFromSource(final String source, final String filename) {
- if ((filename != null) && !filename.isEmpty() && (source != null) && !source.isEmpty()) {
- try {
- InputSource inputSource = new InputSource();
- inputSource.setCharacterStream(new StringReader(stripInvalidProlog(source)));
-
- Document doc = FACTORY.newDocumentBuilder().parse(inputSource);
-
- // See CSL#canFormatBibliographies, checks if the tag exists
- NodeList bibs = doc.getElementsByTagName("bibliography");
- if (bibs.getLength() <= 0) {
- LOGGER.debug("no bibliography element for file {} ", filename);
- return Optional.empty();
- }
-
- NodeList nodes = doc.getElementsByTagName("info");
- NodeList titleNode = ((Element) nodes.item(0)).getElementsByTagName("title");
- String title = ((CharacterData) titleNode.item(0).getFirstChild()).getData();
-
- return Optional.of(new CitationStyle(filename, title, source));
- } catch (ParserConfigurationException | SAXException | IOException e) {
- LOGGER.error("Error while parsing source", e);
+ private static Optional createCitationStyleFromSource(final InputStream source, final String filename) {
+ try {
+ // We need the content twice:
+ // First, for parsing it here for the name
+ // Second for the CSL library to parse it
+ String content = new String(source.readAllBytes());
+
+ Optional title = getTitle(filename, content);
+ if (title.isEmpty()) {
+ return Optional.empty();
}
+
+ return Optional.of(new CitationStyle(filename, title.get(), content));
+ } catch (ParserConfigurationException | SAXException | IOException e) {
+ LOGGER.error("Error while parsing source", e);
+ return Optional.empty();
}
- return Optional.empty();
+ }
+
+ private static Optional getTitle(String filename, String content) throws SAXException, IOException, ParserConfigurationException {
+ // TODO: Switch to StAX parsing (to speed up - we need only the title)
+ InputSource inputSource = new InputSource(new StringReader(content));
+ Document doc = FACTORY.newDocumentBuilder().parse(inputSource);
+
+ // See CSL#canFormatBibliographies, checks if the tag exists
+ NodeList bibs = doc.getElementsByTagName("bibliography");
+ if (bibs.getLength() <= 0) {
+ LOGGER.debug("no bibliography element for file {} ", filename);
+ return Optional.empty();
+ }
+
+ NodeList nodes = doc.getElementsByTagName("info");
+ NodeList titleNode = ((Element) nodes.item(0)).getElementsByTagName("title");
+ String title = ((CharacterData) titleNode.item(0).getFirstChild()).getData();
+ return Optional.of(title);
}
private static String stripInvalidProlog(String source) {
@@ -102,18 +113,11 @@ public static Optional createCitationStyleFromFile(final String s
return Optional.empty();
}
- try {
- String text;
- String internalFile = STYLES_ROOT + (styleFile.startsWith("/") ? "" : "/") + styleFile;
- URL url = CitationStyle.class.getResource(internalFile);
-
- if (url != null) {
- text = CSLUtils.readURLToString(url, StandardCharsets.UTF_8.toString());
- } else {
- // if the url is null then the style is located outside the classpath
- text = Files.readString(Path.of(styleFile));
- }
- return createCitationStyleFromSource(text, styleFile);
+ String internalFile = STYLES_ROOT + (styleFile.startsWith("/") ? "" : "/") + styleFile;
+ Path internalFilePath = Path.of(internalFile);
+ boolean isExternalFile = Files.exists(internalFilePath);
+ try (InputStream inputStream = isExternalFile ? Files.newInputStream(internalFilePath) : CitationStyle.class.getResourceAsStream(internalFile)) {
+ return createCitationStyleFromSource(inputStream, styleFile);
} catch (NoSuchFileException e) {
LOGGER.error("Could not find file: {}", styleFile, e);
} catch (IOException e) {
@@ -141,6 +145,8 @@ public static List discoverCitationStyles() {
return STYLES;
}
+ // TODO: The list of files should be determined at build time (instead of the dynamic method in discoverCitationStylesInPath(path))
+
URL url = CitationStyle.class.getResource(STYLES_ROOT + DEFAULT);
if (url == null) {
LOGGER.error("Could not find any citation style. Tried with {}.", DEFAULT);
diff --git a/src/main/java/org/jabref/logic/citationstyle/JabRefLocaleProvider.java b/src/main/java/org/jabref/logic/citationstyle/JabRefLocaleProvider.java
index 142b87bb375..01545c06c8f 100644
--- a/src/main/java/org/jabref/logic/citationstyle/JabRefLocaleProvider.java
+++ b/src/main/java/org/jabref/logic/citationstyle/JabRefLocaleProvider.java
@@ -1,13 +1,14 @@
package org.jabref.logic.citationstyle;
import java.io.IOException;
+import java.io.InputStream;
import java.io.UncheckedIOException;
-import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import de.undercouch.citeproc.LocaleProvider;
-import de.undercouch.citeproc.helper.CSLUtils;
+import org.slf4j.Logger;
/**
* A {@link LocaleProvider} that loads locales from a directory in the current module.
@@ -16,6 +17,8 @@
*/
public class JabRefLocaleProvider implements LocaleProvider {
+ private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(JabRefLocaleProvider.class);
+
private static final String LOCALES_ROOT = "/csl-locales";
private final Map locales = new HashMap<>();
@@ -23,13 +26,13 @@ public class JabRefLocaleProvider implements LocaleProvider {
@Override
public String retrieveLocale(String lang) {
return locales.computeIfAbsent(lang, locale -> {
- try {
- URL url = getClass().getResource(LOCALES_ROOT + "/locales-" + locale + ".xml");
- if (url == null) {
+ try (InputStream inputStream = getClass().getResourceAsStream(LOCALES_ROOT + "/locales-" + locale + ".xml")) {
+ if (inputStream == null) {
throw new IllegalArgumentException("Unable to load locale " + locale);
}
- return CSLUtils.readURLToString(url, "UTF-8");
+ return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
} catch (IOException e) {
+ LOGGER.error("failed to read locale {}", locale, e);
throw new UncheckedIOException("failed to read locale " + locale, e);
}
});
diff --git a/src/main/java/org/jabref/logic/exporter/OpenDocumentSpreadsheetCreator.java b/src/main/java/org/jabref/logic/exporter/OpenDocumentSpreadsheetCreator.java
index f8462664548..d58c57a7331 100644
--- a/src/main/java/org/jabref/logic/exporter/OpenDocumentSpreadsheetCreator.java
+++ b/src/main/java/org/jabref/logic/exporter/OpenDocumentSpreadsheetCreator.java
@@ -10,7 +10,6 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
-import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -129,18 +128,8 @@ private static void addResourceFile(String name, String resource, ZipOutputStrea
}
private static void addFromResource(String resource, OutputStream out) {
- URL url = OpenDocumentSpreadsheetCreator.class.getResource(resource);
- try (InputStream in = url.openStream()) {
- byte[] buffer = new byte[256];
- synchronized (out) {
- while (true) {
- int bytesRead = in.read(buffer);
- if (bytesRead == -1) {
- break;
- }
- out.write(buffer, 0, bytesRead);
- }
- }
+ try (InputStream in = OpenDocumentSpreadsheetCreator.class.getResourceAsStream(resource)) {
+ in.transferTo(out);
} catch (IOException e) {
LOGGER.warn("Cannot get resource", e);
}
diff --git a/src/main/java/org/jabref/logic/exporter/OpenOfficeDocumentCreator.java b/src/main/java/org/jabref/logic/exporter/OpenOfficeDocumentCreator.java
index 0ab35a55bd0..e31cb7bd243 100644
--- a/src/main/java/org/jabref/logic/exporter/OpenOfficeDocumentCreator.java
+++ b/src/main/java/org/jabref/logic/exporter/OpenOfficeDocumentCreator.java
@@ -10,7 +10,6 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
-import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -111,18 +110,8 @@ private static void addResourceFile(String name, String resource, ZipOutputStrea
}
private static void addFromResource(String resource, OutputStream out) {
- URL url = OpenOfficeDocumentCreator.class.getResource(resource);
- try (InputStream in = url.openStream()) {
- byte[] buffer = new byte[256];
- synchronized (out) {
- while (true) {
- int bytesRead = in.read(buffer);
- if (bytesRead == -1) {
- break;
- }
- out.write(buffer, 0, bytesRead);
- }
- }
+ try (InputStream in = OpenOfficeDocumentCreator.class.getResourceAsStream(resource)) {
+ in.transferTo(out);
} catch (IOException e) {
LOGGER.warn("Cannot get resource", e);
}
diff --git a/src/main/java/org/jabref/logic/exporter/TemplateExporter.java b/src/main/java/org/jabref/logic/exporter/TemplateExporter.java
index d9d0c33766d..a2714245b99 100644
--- a/src/main/java/org/jabref/logic/exporter/TemplateExporter.java
+++ b/src/main/java/org/jabref/logic/exporter/TemplateExporter.java
@@ -1,10 +1,10 @@
package org.jabref.logic.exporter;
-import java.io.FileNotFoundException;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.Reader;
-import java.net.URISyntaxException;
-import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -161,7 +161,7 @@ public void setCustomExport(boolean custom) {
*
* @param filename the filename
* @return a newly created reader
- * @throws IOException if the reader could not be created
+ * @throws IOException if the reader could not be created (e.g., file is not found)
*/
private Reader getReader(String filename) throws IOException {
// If this is a custom export, just use the given filename:
@@ -171,24 +171,24 @@ private Reader getReader(String filename) throws IOException {
} else {
dir = LAYOUT_PREFIX + (directory == null ? "" : directory + '/');
}
+
// Attempt to get a Reader for the file path given, either by
// loading it as a resource (from within JAR), or as a normal file. If
// unsuccessful (e.g. file not found), an IOException is thrown.
+
String name = dir + filename;
- // Try loading as a resource first. This works for files inside the JAR:
- // If that did not work, try loading as a normal file URL:
- try {
- URL res = TemplateExporter.class.getResource(name);
- Path reso;
- if (res == null) {
- reso = Path.of(name);
- } else {
- reso = Path.of(res.toURI());
- }
- return Files.newBufferedReader(reso, StandardCharsets.UTF_8);
- } catch (FileNotFoundException | URISyntaxException ex) {
+
+ Path path = Path.of(name);
+ if (Files.exists(path)) {
+ return Files.newBufferedReader(path, StandardCharsets.UTF_8);
+ }
+
+ InputStream inputStream = TemplateExporter.class.getResourceAsStream(name);
+ if (inputStream == null) {
throw new IOException("Cannot find layout file: '" + name + "'.");
}
+
+ return new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
}
@Override
diff --git a/src/main/java/org/jabref/logic/git/GitHandler.java b/src/main/java/org/jabref/logic/git/GitHandler.java
index e865e9b9299..77a78681428 100644
--- a/src/main/java/org/jabref/logic/git/GitHandler.java
+++ b/src/main/java/org/jabref/logic/git/GitHandler.java
@@ -2,13 +2,11 @@
import java.io.File;
import java.io.IOException;
-import java.net.URISyntaxException;
+import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
-import org.jabref.logic.util.io.FileUtil;
-
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.RmCommand;
import org.eclipse.jgit.api.Status;
@@ -65,13 +63,13 @@ public GitHandler(Path repositoryPath) {
}
void setupGitIgnore() {
- try {
- Path gitignore = Path.of(repositoryPath.toString(), ".gitignore");
- if (!Files.exists(gitignore)) {
- FileUtil.copyFile(Path.of(this.getClass().getResource("git.gitignore").toURI()), gitignore, false);
+ Path gitignore = Path.of(repositoryPath.toString(), ".gitignore");
+ if (!Files.exists(gitignore)) {
+ try (InputStream inputStream = this.getClass().getResourceAsStream("git.gitignore")) {
+ Files.copy(inputStream, gitignore);
+ } catch (IOException e) {
+ LOGGER.error("Error occurred during copying of the gitignore file into the git repository.", e);
}
- } catch (URISyntaxException e) {
- LOGGER.error("Error occurred during copying of the gitignore file into the git repository.", e);
}
}
diff --git a/src/main/java/org/jabref/logic/importer/util/MathMLParser.java b/src/main/java/org/jabref/logic/importer/util/MathMLParser.java
index 039acf47155..39975d0ceed 100644
--- a/src/main/java/org/jabref/logic/importer/util/MathMLParser.java
+++ b/src/main/java/org/jabref/logic/importer/util/MathMLParser.java
@@ -1,25 +1,23 @@
package org.jabref.logic.importer.util;
-import java.io.IOException;
+import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.Objects;
-import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
+import org.jabref.architecture.AllowedToUseClassGetResource;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+@AllowedToUseClassGetResource("to determine the root directory")
public class MathMLParser {
private static final Logger LOGGER = LoggerFactory.getLogger(MathMLParser.class);
private static final String XSLT_FILE_PATH = "/xslt/mathml_latex/mmltex.xsl";
@@ -33,17 +31,17 @@ public class MathMLParser {
*/
public static String parse(XMLStreamReader reader) {
String xmlContent = "";
- String latexResult = "";
+ String latexResult;
- try {
+ try (InputStream xsltResource = MathMLParser.class.getResourceAsStream(XSLT_FILE_PATH)) {
// extract XML content
xmlContent = StaxParser.getXMLContent(reader);
// convert to LaTeX using XSLT file
Source xmlSource = new StreamSource(new StringReader(xmlContent));
- URL xsltResource = MathMLParser.class.getResource(XSLT_FILE_PATH);
- Source xsltSource = new StreamSource(Objects.requireNonNull(xsltResource).openStream(), xsltResource.toURI().toASCIIString());
+ // No SystemId required, because no relative URLs need to be resolved
+ Source xsltSource = new StreamSource(xsltResource, MathMLParser.class.getResource(XSLT_FILE_PATH).toExternalForm());
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer(xsltSource);
@@ -53,14 +51,9 @@ public static String parse(XMLStreamReader reader) {
transformer.transform(xmlSource, result);
latexResult = writer.getBuffer().toString();
- } catch (XMLStreamException e) {
- LOGGER.debug("An exception occurred when getting XML content", e);
- } catch (IOException e) {
- LOGGER.debug("An I/O exception occurred", e);
- } catch (URISyntaxException e) {
- LOGGER.debug("XSLT Source URI invalid", e);
- } catch (TransformerException e) {
- LOGGER.debug("An exception occurred during transformation", e);
+ } catch (Exception e) {
+ LOGGER.error("Could not transform", e);
+ return "";
}
return latexResult;
diff --git a/src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java b/src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java
index d88bb64fba8..4e47623846e 100644
--- a/src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java
+++ b/src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java
@@ -3,7 +3,6 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyManagementException;
@@ -148,9 +147,10 @@ public static void createTruststoreFileIfNotExist(Path storePath) {
try {
LOGGER.debug("Trust store path: {}", storePath.toAbsolutePath());
if (Files.notExists(storePath)) {
- Path storeResourcePath = Path.of(TrustStoreManager.class.getResource("/ssl/truststore.jks").toURI());
Files.createDirectories(storePath.getParent());
- Files.copy(storeResourcePath, storePath);
+ try (InputStream inputStream = TrustStoreManager.class.getResourceAsStream("/ssl/truststore.jks")) {
+ Files.copy(inputStream, storePath);
+ }
}
try {
@@ -160,8 +160,6 @@ public static void createTruststoreFileIfNotExist(Path storePath) {
}
} catch (IOException e) {
LOGGER.warn("Bad truststore path", e);
- } catch (URISyntaxException e) {
- LOGGER.warn("Bad resource path", e);
}
}
diff --git a/src/main/java/org/jabref/logic/protectedterms/ProtectedTermsLoader.java b/src/main/java/org/jabref/logic/protectedterms/ProtectedTermsLoader.java
index 9f74b266ead..1a0efbb005a 100644
--- a/src/main/java/org/jabref/logic/protectedterms/ProtectedTermsLoader.java
+++ b/src/main/java/org/jabref/logic/protectedterms/ProtectedTermsLoader.java
@@ -112,8 +112,8 @@ public List getProtectedTerms() {
return new ArrayList<>(result);
}
- public void addProtectedTermsListFromFile(String fileName, boolean enabled) {
- mainList.add(readProtectedTermsListFromFile(Path.of(fileName), enabled));
+ public void addProtectedTermsListFromFile(Path path, boolean enabled) {
+ mainList.add(readProtectedTermsListFromFile(path, enabled));
}
public static ProtectedTermsList readProtectedTermsListFromResource(String resource, String description, boolean enabled) {
diff --git a/src/main/java/org/jabref/logic/protectedterms/ProtectedTermsParser.java b/src/main/java/org/jabref/logic/protectedterms/ProtectedTermsParser.java
index e0e9a54dc33..de4d7006e9e 100644
--- a/src/main/java/org/jabref/logic/protectedterms/ProtectedTermsParser.java
+++ b/src/main/java/org/jabref/logic/protectedterms/ProtectedTermsParser.java
@@ -1,14 +1,15 @@
package org.jabref.logic.protectedterms;
+import java.io.BufferedReader;
import java.io.IOException;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jabref.logic.l10n.Localization;
@@ -29,30 +30,39 @@ public class ProtectedTermsParser {
private String location;
public void readTermsFromResource(String resourceFileName, String descriptionString) {
- try {
- Path path = Path.of(Objects.requireNonNull(ProtectedTermsLoader.class.getResource(Objects.requireNonNull(resourceFileName))).toURI());
- readTermsList(path);
- description = descriptionString;
- location = resourceFileName;
- } catch (URISyntaxException e1) {
- LOGGER.error("");
+ description = descriptionString;
+ location = resourceFileName;
+ try (InputStream inputStream = ProtectedTermsLoader.class.getResourceAsStream(Objects.requireNonNull(resourceFileName))) {
+ if (inputStream == null) {
+ LOGGER.error("Cannot find resource '{}' ({})", resourceFileName, descriptionString);
+ return;
+ }
+ readTermsList(inputStream);
+ } catch (IOException e) {
+ LOGGER.error("Cannot open resource '{}'", resourceFileName, e);
}
}
public void readTermsFromFile(Path path) {
- location = path.toAbsolutePath().toString();
- readTermsList(path);
- }
+ location = path.toString();
- private void readTermsList(Path path) {
+ path = path.toAbsolutePath();
if (!Files.exists(path)) {
LOGGER.warn("Could not read terms from file {}", path);
return;
}
- try (Stream lines = Files.lines(path, StandardCharsets.UTF_8)) {
- this.terms.addAll(lines.map(this::setDescription).filter(Objects::nonNull).collect(Collectors.toList()));
+ try (InputStream inputStream = Files.newInputStream(path)) {
+ readTermsList(inputStream);
} catch (IOException e) {
- LOGGER.warn("Could not read terms from file {}", path, e);
+ LOGGER.error("Cannot open file '{}'", path, e);
+ }
+ }
+
+ private void readTermsList(InputStream inputStream) {
+ try (Stream lines = new BufferedReader(new InputStreamReader(inputStream)).lines()) {
+ this.terms.addAll(lines.map(this::setDescription).filter(Objects::nonNull).toList());
+ } catch (UncheckedIOException e) {
+ LOGGER.warn("Could not read terms from stream", e);
}
}
diff --git a/src/main/java/org/jabref/logic/util/io/FileUtil.java b/src/main/java/org/jabref/logic/util/io/FileUtil.java
index b5e75c42172..65f0fac7c23 100644
--- a/src/main/java/org/jabref/logic/util/io/FileUtil.java
+++ b/src/main/java/org/jabref/logic/util/io/FileUtil.java
@@ -7,7 +7,7 @@
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
+import java.nio.file.StandardCopyOption;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
@@ -225,9 +225,8 @@ public static boolean copyFile(Path pathToSourceFile, Path pathToDestinationFile
return false;
}
try {
- // Preserve Hard Links with OpenOption defaults included for clarity
- Files.write(pathToDestinationFile, Files.readAllBytes(pathToSourceFile),
- StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
+ // This should also preserve Hard Links
+ Files.copy(pathToSourceFile, pathToDestinationFile, StandardCopyOption.REPLACE_EXISTING);
return true;
} catch (IOException e) {
LOGGER.error("Copying Files failed.", e);
diff --git a/src/test/java/org/jabref/architecture/MainArchitectureTest.java b/src/test/java/org/jabref/architecture/MainArchitectureTest.java
index 770e5ac4341..5a4e5e64704 100644
--- a/src/test/java/org/jabref/architecture/MainArchitectureTest.java
+++ b/src/test/java/org/jabref/architecture/MainArchitectureTest.java
@@ -2,6 +2,8 @@
import java.nio.file.Paths;
+import org.jabref.logic.importer.fileformat.ImporterTestEngine;
+
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchIgnore;
@@ -26,14 +28,14 @@ class MainArchitectureTest {
private static final String PACKAGE_ORG_JABREF_CLI = "org.jabref.cli..";
@ArchTest
- public static void doNotUseApacheCommonsLang3(JavaClasses classes) {
+ public void doNotUseApacheCommonsLang3(JavaClasses classes) {
noClasses().that().areNotAnnotatedWith(ApacheCommonsLang3Allowed.class)
.should().accessClassesThat().resideInAPackage("org.apache.commons.lang3")
.check(classes);
}
@ArchTest
- public static void doNotUseSwing(JavaClasses classes) {
+ public void doNotUseSwing(JavaClasses classes) {
// This checks for all Swing packages, but not the UndoManager
noClasses().that().areNotAnnotatedWith(AllowedToUseSwing.class)
.should().accessClassesThat()
@@ -50,20 +52,20 @@ public static void doNotUseSwing(JavaClasses classes) {
}
@ArchTest
- public static void doNotUseAssertJ(JavaClasses classes) {
+ public void doNotUseAssertJ(JavaClasses classes) {
noClasses().should().accessClassesThat().resideInAPackage("org.assertj..")
.check(classes);
}
@ArchTest
- public static void doNotUseJavaAWT(JavaClasses classes) {
+ public void doNotUseJavaAWT(JavaClasses classes) {
noClasses().that().areNotAnnotatedWith(AllowedToUseAwt.class)
.should().accessClassesThat().resideInAPackage(PACKAGE_JAVA_AWT)
.check(classes);
}
@ArchTest
- public static void doNotUsePaths(JavaClasses classes) {
+ public void doNotUsePaths(JavaClasses classes) {
noClasses().should()
.accessClassesThat()
.belongToAnyOf(Paths.class)
@@ -71,10 +73,22 @@ public static void doNotUsePaths(JavaClasses classes) {
.check(classes);
}
+ @ArchTest
+ public void useStreamsOfResources(JavaClasses classes) {
+ // Reason: https://github.com/oracle/graal/issues/7682#issuecomment-1786704111
+ noClasses().that().haveNameNotMatching(".*Test")
+ .and().areNotAnnotatedWith(AllowedToUseClassGetResource.class)
+ .and().areNotAssignableFrom(ImporterTestEngine.class)
+ .should()
+ .callMethod(Class.class, "getResource", String.class)
+ .because("getResourceAsStream(...) should be used instead")
+ .check(classes);
+ }
+
@ArchTest
@ArchIgnore
// Fails currently
- public static void respectLayeredArchitecture(JavaClasses classes) {
+ public void respectLayeredArchitecture(JavaClasses classes) {
layeredArchitecture().consideringOnlyDependenciesInLayers()
.layer("Gui").definedBy(PACKAGE_ORG_JABREF_GUI)
.layer("Logic").definedBy(PACKAGE_ORG_JABREF_LOGIC)
@@ -95,7 +109,7 @@ public static void respectLayeredArchitecture(JavaClasses classes) {
}
@ArchTest
- public static void doNotUseLogicInModel(JavaClasses classes) {
+ public void doNotUseLogicInModel(JavaClasses classes) {
noClasses().that().resideInAPackage(PACKAGE_ORG_JABREF_MODEL)
.and().areNotAnnotatedWith(AllowedToUseLogic.class)
.should().dependOnClassesThat().resideInAPackage(PACKAGE_ORG_JABREF_LOGIC)
@@ -103,7 +117,7 @@ public static void doNotUseLogicInModel(JavaClasses classes) {
}
@ArchTest
- public static void restrictUsagesInModel(JavaClasses classes) {
+ public void restrictUsagesInModel(JavaClasses classes) {
// Until we switch to Lucene, we need to access Globals.stateManager().getActiveDatabase() from the search classes,
// because the PDFSearch needs to access the index of the corresponding database
noClasses().that().areNotAssignableFrom("org.jabref.model.search.rules.ContainBasedSearchRule")
@@ -116,7 +130,7 @@ public static void restrictUsagesInModel(JavaClasses classes) {
}
@ArchTest
- public static void restrictUsagesInLogic(JavaClasses classes) {
+ public void restrictUsagesInLogic(JavaClasses classes) {
noClasses().that().resideInAPackage(PACKAGE_ORG_JABREF_LOGIC)
.and().areNotAnnotatedWith(AllowedToUseSwing.class)
.and().areNotAssignableFrom("org.jabref.logic.search.DatabaseSearcherWithBibFilesTest")
@@ -126,7 +140,7 @@ public static void restrictUsagesInLogic(JavaClasses classes) {
}
@ArchTest
- public static void restrictStandardStreams(JavaClasses classes) {
+ public void restrictStandardStreams(JavaClasses classes) {
noClasses().that().resideOutsideOfPackages(PACKAGE_ORG_JABREF_CLI)
.and().resideOutsideOfPackages("org.jabref.gui.openoffice..") // Uses LibreOffice SDK
.and().areNotAnnotatedWith(AllowedToUseStandardStreams.class)
@@ -136,7 +150,7 @@ public static void restrictStandardStreams(JavaClasses classes) {
}
@ArchTest
- public static void nativeDesktopIsRestricted(JavaClasses classes) {
+ public void nativeDesktopIsRestricted(JavaClasses classes) {
noClasses().that().doNotHaveSimpleName("JabRefDesktop")
.and().doNotHaveSimpleName("Launcher")
.and().doNotHaveSimpleName("DefaultDesktop")
diff --git a/src/test/java/org/jabref/logic/protectedterms/ProtectedTermsLoaderTest.java b/src/test/java/org/jabref/logic/protectedterms/ProtectedTermsLoaderTest.java
index 3b7a267ccc3..b3fdacd0dd7 100644
--- a/src/test/java/org/jabref/logic/protectedterms/ProtectedTermsLoaderTest.java
+++ b/src/test/java/org/jabref/logic/protectedterms/ProtectedTermsLoaderTest.java
@@ -34,20 +34,18 @@ void getProtectedTerms() throws URISyntaxException {
loader.removeProtectedTermsList(list);
}
assertTrue(loader.getProtectedTermsLists().isEmpty());
- String filename = Path.of(ProtectedTermsLoader.class.getResource("/org/jabref/logic/protectedterms/namedterms.terms")
- .toURI())
- .toFile().getPath();
- loader.addProtectedTermsListFromFile(filename, true);
+ Path path = Path.of(ProtectedTermsLoader.class.getResource("/org/jabref/logic/protectedterms/namedterms.terms")
+ .toURI());
+ loader.addProtectedTermsListFromFile(path, true);
assertEquals(List.of("Einstein"), loader.getProtectedTerms());
}
@Test
void addProtectedTermsListFromFile() throws URISyntaxException {
- String filename = Path.of(ProtectedTermsLoader.class.getResource("/org/jabref/logic/protectedterms/namedterms.terms")
- .toURI())
- .toFile().getPath();
+ Path path = Path.of(ProtectedTermsLoader.class.getResource("/org/jabref/logic/protectedterms/namedterms.terms")
+ .toURI());
assertEquals(ProtectedTermsLoader.getInternalLists().size(), loader.getProtectedTermsLists().size());
- loader.addProtectedTermsListFromFile(filename, false);
+ loader.addProtectedTermsListFromFile(path, false);
assertEquals(ProtectedTermsLoader.getInternalLists().size() + 1, loader.getProtectedTermsLists().size());
}