-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8129 from colinhex/generateEntryFromId
Generate an entry from ID
- Loading branch information
Showing
12 changed files
with
514 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package org.jabref.gui.importer; | ||
|
||
import java.util.Optional; | ||
|
||
import org.jabref.gui.DialogService; | ||
import org.jabref.gui.Globals; | ||
import org.jabref.gui.LibraryTab; | ||
import org.jabref.gui.actions.SimpleCommand; | ||
import org.jabref.gui.util.BackgroundTask; | ||
import org.jabref.gui.util.TaskExecutor; | ||
import org.jabref.logic.JabRefException; | ||
import org.jabref.logic.database.DuplicateCheck; | ||
import org.jabref.logic.importer.CompositeIdFetcher; | ||
import org.jabref.logic.importer.FetcherException; | ||
import org.jabref.logic.importer.ImportCleanup; | ||
import org.jabref.logic.l10n.Localization; | ||
import org.jabref.model.entry.BibEntry; | ||
import org.jabref.preferences.PreferencesService; | ||
|
||
import org.controlsfx.control.PopOver; | ||
|
||
public class GenerateEntryFromIdAction extends SimpleCommand { | ||
|
||
private final LibraryTab libraryTab; | ||
private final DialogService dialogService; | ||
private final PreferencesService preferencesService; | ||
private final String identifier; | ||
private final TaskExecutor taskExecutor; | ||
private final PopOver entryFromIdPopOver; | ||
|
||
public GenerateEntryFromIdAction(LibraryTab libraryTab, DialogService dialogService, PreferencesService preferencesService, TaskExecutor taskExecutor, PopOver entryFromIdPopOver, String identifier) { | ||
this.libraryTab = libraryTab; | ||
this.dialogService = dialogService; | ||
this.preferencesService = preferencesService; | ||
this.identifier = identifier; | ||
this.taskExecutor = taskExecutor; | ||
this.entryFromIdPopOver = entryFromIdPopOver; | ||
} | ||
|
||
@Override | ||
public void execute() { | ||
BackgroundTask<Optional<BibEntry>> backgroundTask = searchAndImportEntryInBackground(); | ||
backgroundTask.titleProperty().set(Localization.lang("Import by ID")); | ||
backgroundTask.showToUser(true); | ||
backgroundTask.onRunning(() -> dialogService.notify("%s".formatted(backgroundTask.messageProperty().get()))); | ||
backgroundTask.onFailure((e) -> dialogService.notify(e.getMessage())); | ||
backgroundTask.onSuccess((bibEntry) -> bibEntry.ifPresentOrElse((entry) -> { | ||
libraryTab.insertEntry(entry); | ||
entryFromIdPopOver.hide(); | ||
dialogService.notify(Localization.lang("Imported one entry")); | ||
}, | ||
() -> dialogService.notify(Localization.lang("Import canceled")) | ||
)); | ||
backgroundTask.executeWith(taskExecutor); | ||
} | ||
|
||
private BackgroundTask<Optional<BibEntry>> searchAndImportEntryInBackground() { | ||
return new BackgroundTask<>() { | ||
@Override | ||
protected Optional<BibEntry> call() throws JabRefException { | ||
if (isCanceled()) { | ||
return Optional.empty(); | ||
} | ||
|
||
updateMessage(Localization.lang("Searching...")); | ||
try { | ||
Optional<BibEntry> result = new CompositeIdFetcher(preferencesService.getImportFormatPreferences()).performSearchById(identifier); | ||
if (result.isPresent()) { | ||
final BibEntry entry = result.get(); | ||
ImportCleanup cleanup = new ImportCleanup(libraryTab.getBibDatabaseContext().getMode()); | ||
cleanup.doPostCleanup(entry); | ||
// DuplicateCheck only covers DOI and ISBN at the moment. | ||
Optional<BibEntry> duplicate = new DuplicateCheck(Globals.entryTypesManager).containsDuplicate(libraryTab.getDatabase(), entry, libraryTab.getBibDatabaseContext().getMode()); | ||
if (duplicate.isPresent()) { | ||
throw new JabRefException(Localization.lang("Entry already exists")); | ||
} | ||
} else { | ||
throw new JabRefException(Localization.lang("Could not find any bibliographic information.")); | ||
} | ||
updateMessage(Localization.lang("Imported one entry")); | ||
return result; | ||
} catch (FetcherException fetcherException) { | ||
throw new JabRefException("Fetcher error: %s".formatted(fetcherException.getMessage())); | ||
} | ||
} | ||
}; | ||
} | ||
|
||
} |
17 changes: 17 additions & 0 deletions
17
src/main/java/org/jabref/gui/importer/GenerateEntryFromIdDialog.fxml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
|
||
<?import javafx.scene.control.Button?> | ||
<?import javafx.scene.control.DialogPane?> | ||
<?import javafx.scene.control.Label?> | ||
<?import javafx.scene.control.TextField?> | ||
<?import javafx.scene.layout.Pane?> | ||
|
||
<DialogPane fx:id="dialogPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="92.0" prefWidth="392.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.jabref.gui.importer.GenerateEntryFromIdDialog"> | ||
<content> | ||
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="102.0" prefWidth="360.0"> | ||
<TextField fx:id="idTextField" layoutX="14.0" layoutY="40.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="29.0" prefWidth="287.0" /> | ||
<Button fx:id="generateButton" layoutX="314.0" layoutY="40.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#generateEntry" prefHeight="30.0" prefWidth="55.0" textAlignment="CENTER" /> | ||
<Label layoutX="14.0" layoutY="14.0" prefHeight="16.0" prefWidth="328.0" text="Import new entry from ID"/> | ||
</Pane> | ||
</content> | ||
</DialogPane> |
69 changes: 69 additions & 0 deletions
69
src/main/java/org/jabref/gui/importer/GenerateEntryFromIdDialog.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package org.jabref.gui.importer; | ||
|
||
import javafx.fxml.FXML; | ||
import javafx.scene.control.Button; | ||
import javafx.scene.control.DialogPane; | ||
import javafx.scene.control.TextField; | ||
|
||
import org.jabref.gui.DialogService; | ||
import org.jabref.gui.LibraryTab; | ||
import org.jabref.gui.icon.IconTheme; | ||
import org.jabref.gui.util.TaskExecutor; | ||
import org.jabref.logic.l10n.Localization; | ||
import org.jabref.preferences.PreferencesService; | ||
|
||
import com.airhacks.afterburner.views.ViewLoader; | ||
import org.controlsfx.control.PopOver; | ||
|
||
public class GenerateEntryFromIdDialog { | ||
|
||
@FXML DialogPane dialogPane; | ||
@FXML TextField idTextField; | ||
@FXML Button generateButton; | ||
|
||
private final PreferencesService preferencesService; | ||
private final DialogService dialogService; | ||
private final LibraryTab libraryTab; | ||
private final TaskExecutor taskExecutor; | ||
|
||
private PopOver entryFromIdPopOver; | ||
|
||
public GenerateEntryFromIdDialog(LibraryTab libraryTab, DialogService dialogService, PreferencesService preferencesService, TaskExecutor taskExecutor) { | ||
ViewLoader.view(this).load(); | ||
this.preferencesService = preferencesService; | ||
this.dialogService = dialogService; | ||
this.libraryTab = libraryTab; | ||
this.taskExecutor = taskExecutor; | ||
|
||
this.generateButton.setGraphic(IconTheme.JabRefIcons.IMPORT.getGraphicNode()); | ||
this.generateButton.setDefaultButton(true); | ||
} | ||
|
||
@FXML private void generateEntry() { | ||
if (idTextField.getText().isEmpty()) { | ||
dialogService.notify(Localization.lang("Enter a valid ID")); | ||
return; | ||
} | ||
|
||
this.idTextField.requestFocus(); | ||
|
||
GenerateEntryFromIdAction generateEntryFromIdAction = new GenerateEntryFromIdAction( | ||
libraryTab, | ||
dialogService, | ||
preferencesService, | ||
taskExecutor, | ||
entryFromIdPopOver, | ||
idTextField.getText() | ||
); | ||
generateEntryFromIdAction.execute(); | ||
} | ||
|
||
public void setEntryFromIdPopOver(PopOver entryFromIdPopOver) { | ||
this.entryFromIdPopOver = entryFromIdPopOver; | ||
} | ||
|
||
public DialogPane getDialogPane() { | ||
return dialogPane; | ||
} | ||
|
||
} |
47 changes: 47 additions & 0 deletions
47
src/main/java/org/jabref/logic/importer/CompositeIdFetcher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package org.jabref.logic.importer; | ||
|
||
import java.util.Optional; | ||
|
||
import org.jabref.logic.importer.fetcher.ArXiv; | ||
import org.jabref.logic.importer.fetcher.DoiFetcher; | ||
import org.jabref.logic.importer.fetcher.IacrEprintFetcher; | ||
import org.jabref.logic.importer.fetcher.IsbnFetcher; | ||
import org.jabref.model.entry.BibEntry; | ||
import org.jabref.model.entry.identifier.ArXivIdentifier; | ||
import org.jabref.model.entry.identifier.DOI; | ||
import org.jabref.model.entry.identifier.ISBN; | ||
import org.jabref.model.entry.identifier.IacrEprint; | ||
|
||
public class CompositeIdFetcher { | ||
|
||
private final ImportFormatPreferences importFormatPreferences; | ||
|
||
public CompositeIdFetcher(ImportFormatPreferences importFormatPreferences) { | ||
this.importFormatPreferences = importFormatPreferences; | ||
} | ||
|
||
public Optional<BibEntry> performSearchById(String identifier) throws FetcherException { | ||
Optional<DOI> doi = DOI.parse(identifier); | ||
if (doi.isPresent()) { | ||
return new DoiFetcher(importFormatPreferences).performSearchById(doi.get().getNormalized()); | ||
} | ||
Optional<ArXivIdentifier> arXivIdentifier = ArXivIdentifier.parse(identifier); | ||
if (arXivIdentifier.isPresent()) { | ||
return new ArXiv(importFormatPreferences).performSearchById(arXivIdentifier.get().getNormalized()); | ||
} | ||
Optional<ISBN> isbn = ISBN.parse(identifier); | ||
if (isbn.isPresent()) { | ||
return new IsbnFetcher(importFormatPreferences).performSearchById(isbn.get().getNormalized()); | ||
} | ||
Optional<IacrEprint> iacrEprint = IacrEprint.parse(identifier); | ||
if (iacrEprint.isPresent()) { | ||
return new IacrEprintFetcher(importFormatPreferences).performSearchById(iacrEprint.get().getNormalized()); | ||
} | ||
|
||
return Optional.empty(); | ||
} | ||
|
||
public String getName() { | ||
return "CompositeIdFetcher"; | ||
} | ||
} |
77 changes: 77 additions & 0 deletions
77
src/main/java/org/jabref/model/entry/identifier/IacrEprint.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package org.jabref.model.entry.identifier; | ||
|
||
import java.net.URI; | ||
import java.net.URISyntaxException; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
import org.jabref.model.entry.field.Field; | ||
import org.jabref.model.entry.field.StandardField; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public class IacrEprint implements Identifier { | ||
public static final URI RESOLVER = URI.create("https://ia.cr"); | ||
private static final Logger LOGGER = LoggerFactory.getLogger(IacrEprint.class); | ||
|
||
private static final String IACR_EPRINT_EXP = "\\d{4}\\/\\d{3,5}"; | ||
private final String iacrEprint; | ||
|
||
IacrEprint(String iacrEprint) { | ||
Objects.requireNonNull(iacrEprint); | ||
|
||
String trimmedId = iacrEprint.trim(); | ||
|
||
if (matchesExcepted(trimmedId)) { | ||
Matcher matcher = Pattern.compile(IACR_EPRINT_EXP).matcher(trimmedId); | ||
matcher.find(); | ||
this.iacrEprint = matcher.group(0); | ||
} else { | ||
throw new IllegalArgumentException(trimmedId + " is not a valid IacrEprint identifier."); | ||
} | ||
} | ||
|
||
private static boolean matchesExcepted(String identifier) { | ||
return identifier.matches( | ||
"(https\\:\\/\\/)?(ia\\.cr\\/|eprint\\.iacr\\.org\\/)?" + IACR_EPRINT_EXP | ||
); | ||
} | ||
|
||
public static Optional<IacrEprint> parse(String identifier) { | ||
String trimmed = identifier.strip(); | ||
try { | ||
return Optional.of(new IacrEprint(trimmed)); | ||
} catch (IllegalArgumentException illegalArgumentException) { | ||
return Optional.empty(); | ||
} | ||
} | ||
|
||
@Override | ||
public String getNormalized() { | ||
return iacrEprint; | ||
} | ||
|
||
@Override | ||
public Field getDefaultField() { | ||
return StandardField.EPRINT; | ||
} | ||
|
||
@Override | ||
public Optional<URI> getExternalURI() { | ||
try { | ||
URI uri = new URI(RESOLVER.getScheme(), RESOLVER.getHost(), "/" + iacrEprint, null); | ||
return Optional.of(uri); | ||
} catch (URISyntaxException e) { | ||
// should never happen | ||
LOGGER.error(iacrEprint + " could not be encoded as URI.", e); | ||
return Optional.empty(); | ||
} | ||
} | ||
|
||
public String getAsciiUrl() { | ||
return getExternalURI().map(URI::toASCIIString).orElse(""); | ||
} | ||
} |
Oops, something went wrong.