Skip to content

Commit

Permalink
LaTeX integration latest changes (#5167)
Browse files Browse the repository at this point in the history
* Update for adding latest changes (no final version)

* Fix small issues

* Update for improving code

* Add localization keys

* Update styles

* Remove unused code and improve exceptions handling

* Add latest improvements
  • Loading branch information
davidemdot authored Aug 2, 2019
1 parent 06d266f commit b0fbb14
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 62 deletions.
2 changes: 1 addition & 1 deletion src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ private List<EntryEditorTab> createTabs() {
tabs.add(sourceTab);

// LaTeX citations tab
tabs.add(new LatexCitationsTab(databaseContext, preferencesService, taskExecutor));
tabs.add(new LatexCitationsTab(databaseContext, preferencesService, taskExecutor, dialogService));

return tabs;
}
Expand Down
32 changes: 20 additions & 12 deletions src/main/java/org/jabref/gui/entryeditor/LatexCitationsTab.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package org.jabref.gui.entryeditor;

import java.nio.file.Path;
import java.util.Optional;
import java.util.stream.Collectors;

import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;

import org.jabref.gui.DialogService;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.l10n.Localization;
Expand All @@ -27,8 +28,8 @@ public class LatexCitationsTab extends EntryEditorTab {
private final ProgressIndicator progressIndicator;

public LatexCitationsTab(BibDatabaseContext databaseContext, PreferencesService preferencesService,
TaskExecutor taskExecutor) {
this.viewModel = new LatexCitationsTabViewModel(databaseContext, preferencesService, taskExecutor);
TaskExecutor taskExecutor, DialogService dialogService) {
this.viewModel = new LatexCitationsTabViewModel(databaseContext, preferencesService, taskExecutor, dialogService);
this.searchPane = new StackPane();
this.progressIndicator = new ProgressIndicator();

Expand Down Expand Up @@ -62,14 +63,25 @@ private void setSearchPane() {
});
}

private VBox getLatexDirectoryBox() {
Text latexDirectoryText = new Text(String.format("%n%n%s: %s", Localization.lang("Current search directory"),
viewModel.directoryProperty().get()));
latexDirectoryText.setStyle("-fx-font-weight: bold;");
Button latexDirectoryButton = new Button(Localization.lang("Set LaTeX file directory"));
latexDirectoryButton.setOnAction(event -> viewModel.setLatexDirectory());

return new VBox(15, latexDirectoryText, latexDirectoryButton);
}

private ScrollPane getCitationsPane() {
Text titleText = new Text(Localization.lang("Citations found"));
titleText.getStyleClass().add("recommendation-heading");

VBox citationsBox = new VBox(20, titleText);
Path basePath = viewModel.directoryProperty().get();
citationsBox.getChildren().addAll(viewModel.getCitationList().stream().map(
citation -> citation.getDisplayGraphic(basePath, Optional.empty())).collect(Collectors.toList()));
citation -> citation.getDisplayGraphic(viewModel.directoryProperty().get(), Optional.empty())).collect(Collectors.toList()));

citationsBox.getChildren().add(getLatexDirectoryBox());

ScrollPane citationsPane = new ScrollPane();
citationsPane.setContent(citationsBox);
Expand All @@ -82,13 +94,9 @@ private ScrollPane getNotFoundPane() {
notFoundTitleText.getStyleClass().add("recommendation-heading");

Text notFoundText = new Text(Localization.lang("No LaTeX files containing this entry were found."));
notFoundText.setStyle("-fx-font-size: 110%");

Text notFoundAdviceText = new Text(Localization.lang(
"You can set the LaTeX file directory in the 'Library properties' dialog."));
notFoundAdviceText.setStyle("-fx-font-weight: bold;");
notFoundText.setStyle("-fx-font-size: 110%;");

VBox notFoundBox = new VBox(20, notFoundTitleText, notFoundText, notFoundAdviceText);
VBox notFoundBox = new VBox(20, notFoundTitleText, notFoundText, getLatexDirectoryBox());
ScrollPane notFoundPane = new ScrollPane();
notFoundPane.setContent(notFoundBox);

Expand All @@ -103,7 +111,7 @@ private ScrollPane getErrorPane() {
Text errorMessageText = new Text(viewModel.searchErrorProperty().get());
errorMessageText.setStyle("-fx-font-family: monospace;-fx-font-size: 120%;");

VBox errorBox = new VBox(20, errorTitleText, errorMessageText);
VBox errorBox = new VBox(20, errorTitleText, errorMessageText, getLatexDirectoryBox());
ScrollPane errorPane = new ScrollPane();
errorPane.setContent(errorBox);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
Expand All @@ -18,8 +20,10 @@
import javafx.collections.ObservableList;

import org.jabref.gui.AbstractViewModel;
import org.jabref.gui.DialogService;
import org.jabref.gui.texparser.CitationViewModel;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.DirectoryDialogConfiguration;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.texparser.DefaultTexParser;
Expand All @@ -45,17 +49,20 @@ enum Status {
private final BibDatabaseContext databaseContext;
private final PreferencesService preferencesService;
private final TaskExecutor taskExecutor;
private final DialogService dialogService;
private final ObjectProperty<Path> directory;
private final ObservableList<CitationViewModel> citationList;
private final ObjectProperty<Status> status;
private final StringProperty searchError;
private Future<?> searchTask;
private TexParserResult texParserResult;

public LatexCitationsTabViewModel(BibDatabaseContext databaseContext, PreferencesService preferencesService,
TaskExecutor taskExecutor) {
TaskExecutor taskExecutor, DialogService dialogService) {
this.databaseContext = databaseContext;
this.preferencesService = preferencesService;
this.taskExecutor = taskExecutor;
this.dialogService = dialogService;
this.directory = new SimpleObjectProperty<>(null);
this.citationList = FXCollections.observableArrayList();
this.status = new SimpleObjectProperty<>(Status.IN_PROGRESS);
Expand Down Expand Up @@ -95,7 +102,7 @@ private void startSearch(String citeKey) {
.onRunning(() -> status.set(Status.IN_PROGRESS))
.onSuccess(status::set)
.onFailure(error -> {
searchError.set(String.format("%s%n%n%s", error.getMessage(), error.getCause()));
searchError.set(error.getMessage());
status.set(Status.ERROR);
})
.executeWith(taskExecutor);
Expand All @@ -111,23 +118,53 @@ private void cancelSearch() {
}

private Status searchAndParse(String citeKey) throws IOException {
directory.set(databaseContext.getMetaData().getLaTexFileDirectory(preferencesService.getUser())
.orElseGet(preferencesService::getWorkingDir));
List<Path> texFiles;
try (Stream<Path> filesStream = Files.walk(directory.get())) {
texFiles = filesStream.filter(path -> path.toFile().isFile() && path.toString().endsWith(TEX_EXT))
.collect(Collectors.toList());
} catch (IOException e) {
LOGGER.error("Error searching files", e);
throw new IOException("Error searching files", e);
Path newDirectory = databaseContext.getMetaData().getLaTexFileDirectory(preferencesService.getUser())
.orElseGet(preferencesService::getWorkingDir);

if (texParserResult == null || !newDirectory.equals(directory.get())) {
directory.set(newDirectory);

if (Files.notExists(newDirectory)) {
throw new IOException(String.format("Current search directory does not exist: %s", newDirectory));
}

List<Path> texFiles = searchDirectory(newDirectory, new ArrayList<>());
texParserResult = new DefaultTexParser().parse(texFiles);
}

TexParserResult texParserResult = new DefaultTexParser().parse(citeKey, texFiles);
citationList.setAll(texParserResult.getCitations().values().stream().map(CitationViewModel::new).collect(Collectors.toList()));
citationList.setAll(texParserResult.getCitationsByKey(citeKey).stream().map(CitationViewModel::new).collect(Collectors.toList()));

return citationList.isEmpty() ? Status.NO_RESULTS : Status.CITATIONS_FOUND;
}

private List<Path> searchDirectory(Path directory, List<Path> texFiles) {
Map<Boolean, List<Path>> fileListPartition;
try (Stream<Path> filesStream = Files.list(directory)) {
fileListPartition = filesStream.collect(Collectors.partitioningBy(path -> path.toFile().isDirectory()));
} catch (IOException e) {
LOGGER.error(String.format("Error searching files: %s", e.getMessage()));
return texFiles;
}

List<Path> subDirectories = fileListPartition.get(true);
List<Path> files = fileListPartition.get(false)
.stream()
.filter(path -> path.toString().endsWith(TEX_EXT))
.collect(Collectors.toList());
texFiles.addAll(files);
subDirectories.forEach(subDirectory -> searchDirectory(subDirectory, texFiles));

return texFiles;
}

public void setLatexDirectory() {
DirectoryDialogConfiguration directoryDialogConfiguration = new DirectoryDialogConfiguration.Builder()
.withInitialDirectory(directory.get()).build();

dialogService.showDirectorySelectionDialog(directoryDialogConfiguration).ifPresent(selectedDirectory ->
databaseContext.getMetaData().setLaTexFileDirectory(preferencesService.getUser(), selectedDirectory.toAbsolutePath()));
}

public boolean shouldShow() {
return preferencesService.getEntryEditorPreferences().shouldShowLatexCitationsTab();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@
import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator;
import de.saxsys.mvvmfx.utils.validation.ValidationMessage;
import de.saxsys.mvvmfx.utils.validation.ValidationStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ParseTexDialogViewModel extends AbstractViewModel {

private static final Logger LOGGER = LoggerFactory.getLogger(ParseTexDialogViewModel.class);
private static final String TEX_EXT = ".tex";
private final DialogService dialogService;
private final TaskExecutor taskExecutor;
Expand Down Expand Up @@ -131,7 +134,7 @@ public void searchButtonClicked() {

private FileNodeViewModel searchDirectory(Path directory) throws IOException {
if (directory == null || !directory.toFile().exists() || !directory.toFile().isDirectory()) {
throw new IOException("Error searching an invalid directory");
throw new IOException(String.format("Error searching files: %s", directory));
}

FileNodeViewModel parent = new FileNodeViewModel(directory);
Expand All @@ -140,7 +143,8 @@ private FileNodeViewModel searchDirectory(Path directory) throws IOException {
try (Stream<Path> filesStream = Files.list(directory)) {
fileListPartition = filesStream.collect(Collectors.partitioningBy(path -> path.toFile().isDirectory()));
} catch (IOException e) {
throw new IOException("Error searching files", e);
LOGGER.error(String.format("Error searching files: %s", e.getMessage()));
return parent;
}

List<Path> subDirectories = fileListPartition.get(true);
Expand Down
53 changes: 20 additions & 33 deletions src/main/java/org/jabref/logic/texparser/DefaultTexParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.IOException;
import java.io.LineNumberReader;
import java.io.UncheckedIOException;
import java.nio.channels.ClosedChannelException;
import java.nio.file.Files;
import java.nio.file.Path;
Expand Down Expand Up @@ -58,56 +59,47 @@ public TexParserResult getTexParserResult() {

@Override
public TexParserResult parse(String citeString) {
matchCitation(null, Paths.get(""), 1, citeString);
matchCitation(Paths.get(""), 1, citeString);
return texParserResult;
}

@Override
public TexParserResult parse(Path texFile) {
return parse(null, Collections.singletonList(texFile));
return parse(Collections.singletonList(texFile));
}

@Override
public TexParserResult parse(List<Path> texFiles) {
return parse(null, texFiles);
}

/**
* Parse a list of TEX files for searching a given entry.
*
* @param entryKey String that contains the entry key we are searching (null for all entries)
* @param texFiles List of Path objects linked to a TEX file
* @return a TexParserResult, which contains all data related to the bibliographic entries
*/
public TexParserResult parse(String entryKey, List<Path> texFiles) {
texParserResult.addFiles(texFiles);

List<Path> referencedFiles = new ArrayList<>();

for (Path file : texFiles) {
if (Files.notExists(file)) {
LOGGER.error(String.format("File does not exist: %s", file));
continue;
}

try (LineNumberReader lineNumberReader = new LineNumberReader(Files.newBufferedReader(file))) {
for (String line = lineNumberReader.readLine(); line != null; line = lineNumberReader.readLine()) {
// Skip comments and blank lines.
if (line.isEmpty() || line.charAt(0) == '%') {
continue;
}
// Skip the citation matching if the line does not contain the given entry to speed up the parsing.
if (entryKey == null || line.contains(entryKey)) {
matchCitation(entryKey, file, lineNumberReader.getLineNumber(), line);
}
matchCitation(file, lineNumberReader.getLineNumber(), line);
matchNestedFile(file, texFiles, referencedFiles, line);
}
} catch (ClosedChannelException e) {
LOGGER.info("Parsing has been interrupted");
return texParserResult;
} catch (IOException e) {
LOGGER.error("Error opening a TEX file", e);
LOGGER.error("Parsing has been interrupted");
return null;
} catch (IOException | UncheckedIOException e) {
LOGGER.error(String.format("Error searching files: %s", e.getMessage()));
}
}

// Parse all files referenced by TEX files, recursively.
if (!referencedFiles.isEmpty()) {
parse(entryKey, referencedFiles);
parse(referencedFiles);
}

return texParserResult;
Expand All @@ -116,12 +108,11 @@ public TexParserResult parse(String entryKey, List<Path> texFiles) {
/**
* Find cites along a specific line and store them.
*/
private void matchCitation(String entryKey, Path file, int lineNumber, String line) {
private void matchCitation(Path file, int lineNumber, String line) {
Matcher citeMatch = CITE_PATTERN.matcher(line);

while (citeMatch.find()) {
Arrays.stream(citeMatch.group(CITE_GROUP).split(","))
.filter(key -> entryKey == null || key.equals(entryKey))
.forEach(key -> texParserResult.addKey(key, file, lineNumber, citeMatch.start(), citeMatch.end(), line));
}
}
Expand All @@ -133,16 +124,12 @@ private void matchNestedFile(Path file, List<Path> texFiles, List<Path> referenc
Matcher includeMatch = INCLUDE_PATTERN.matcher(line);

while (includeMatch.find()) {
StringBuilder include = new StringBuilder(includeMatch.group(INCLUDE_GROUP));

if (!include.toString().endsWith(TEX_EXT)) {
include.append(TEX_EXT);
}
String include = includeMatch.group(INCLUDE_GROUP);

Path folder = file.getParent();
Path inputFile = (folder == null)
? Paths.get(include.toString())
: folder.resolve(include.toString());
Path inputFile = file.toAbsolutePath().getParent().resolve(
include.endsWith(TEX_EXT)
? include
: String.format("%s%s", include, TEX_EXT));

if (!texFiles.contains(inputFile)) {
referencedFiles.add(inputFile);
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/l10n/JabRef_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2120,5 +2120,6 @@ Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=Search citations for this
Citations\ found=Citations found
No\ citations\ found=No citations found
No\ LaTeX\ files\ containing\ this\ entry\ were\ found.=No LaTeX files containing this entry were found.
You\ can\ set\ the\ LaTeX\ file\ directory\ in\ the\ 'Library\ properties'\ dialog.=You can set the LaTeX file directory in the 'Library properties' dialog.
Selected\ entry\ does\ not\ have\ an\ associated\ BibTeX\ key.=Selected entry does not have an associated BibTeX key.
Current\ search\ directory=Current search directory
Set\ LaTeX\ file\ directory=Set LaTeX file directory

0 comments on commit b0fbb14

Please sign in to comment.