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()); }