-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix for issue 3143: Import entry from clipboard in different formats #3243
Changes from 4 commits
dfa0bd3
d99f395
2730b8b
b3c2283
26a6f81
0a8dea5
bb2f7d0
4ffc61e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
package org.jabref.logic.importer; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.io.StringReader; | ||
import java.nio.file.Path; | ||
import java.util.List; | ||
import java.util.Objects; | ||
|
@@ -31,6 +33,7 @@ | |
import org.jabref.logic.xmp.XMPPreferences; | ||
import org.jabref.model.database.BibDatabases; | ||
import org.jabref.model.entry.BibEntry; | ||
import org.jabref.model.entry.FieldName; | ||
import org.jabref.model.strings.StringUtil; | ||
|
||
public class ImportFormatReader { | ||
|
@@ -215,4 +218,39 @@ public UnknownFormatImport importUnknownFormat(Path filePath) throws ImportExcep | |
|
||
throw new ImportException(Localization.lang("Could not find a suitable import format.")); | ||
} | ||
|
||
public UnknownFormatImport importUnknownFormatFromString(String data) throws ImportException { | ||
// stores ref to best result, gets updated at the next loop | ||
List<BibEntry> bestResult = null; | ||
long bestResultCount = 0; | ||
String bestFormatName = null; | ||
|
||
// Cycle through all importers: | ||
for (Importer imFo : getImportFormats()) { | ||
try (StringReader in = new StringReader(data); BufferedReader input = new BufferedReader(in)) { | ||
ParserResult parserResult = imFo.importDatabase(input); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please extract the method |
||
List<BibEntry> entries = parserResult.getDatabase().getEntries(); | ||
|
||
BibDatabases.purgeEmptyEntries(entries); | ||
// some parsers return non-empty entries even if the string is in a wrong format. While this prevents pasting of databases where all entries have no title or no author, it prevents false positives | ||
long entryCount = entries.stream().filter(bibEntry -> bibEntry.getField(FieldName.TITLE).isPresent() && bibEntry.getField(FieldName.AUTHOR).isPresent()).count(); | ||
|
||
if (entryCount > bestResultCount) { | ||
bestResult = entries; | ||
bestResultCount = entryCount; | ||
bestFormatName = imFo.getName(); | ||
} | ||
} catch (IOException | UnsupportedOperationException | IllegalArgumentException ex) { | ||
// The import did not succeed. Go on. | ||
} | ||
} | ||
|
||
if (bestResult != null) { | ||
// we found something | ||
ParserResult parserResult = new ParserResult(bestResult); | ||
return new UnknownFormatImport(bestFormatName, parserResult); | ||
} | ||
|
||
throw new ImportException(Localization.lang("Could not find a suitable import format.")); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,85 @@ | ||||
package org.jabref.gui; | ||||
|
||||
import java.awt.datatransfer.Clipboard; | ||||
import java.awt.datatransfer.DataFlavor; | ||||
import java.awt.datatransfer.Transferable; | ||||
import java.lang.reflect.Field; | ||||
import java.lang.reflect.Modifier; | ||||
import java.util.Arrays; | ||||
import java.util.List; | ||||
|
||||
import org.jabref.logic.importer.ImportException; | ||||
import org.jabref.logic.importer.ImportFormatReader; | ||||
import org.jabref.logic.importer.ImportFormatReader.UnknownFormatImport; | ||||
import org.jabref.logic.importer.ParserResult; | ||||
import org.jabref.model.entry.BibEntry; | ||||
|
||||
import org.junit.Before; | ||||
import org.junit.Test; | ||||
import org.mockito.ArgumentMatchers; | ||||
|
||||
import static org.junit.Assert.*; | ||||
import static org.mockito.Mockito.mock; | ||||
import static org.mockito.Mockito.when; | ||||
|
||||
public class ClipBoardManagerTest { | ||||
|
||||
private ClipBoardManager clipBoardManager; | ||||
private Transferable content; | ||||
private ImportFormatReader importFormatReader; | ||||
|
||||
@Before | ||||
public void setUp() throws Exception { | ||||
importFormatReader = mock(ImportFormatReader.class); | ||||
|
||||
clipBoardManager = new ClipBoardManager(importFormatReader); | ||||
Clipboard clipboard = mock(Clipboard.class); | ||||
Field field = ClipBoardManager.class.getDeclaredField("CLIPBOARD"); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a big fan of these tests! The only thing that I don't like is this hack to set
to an instance variable which is initialized in the constructor. Then you can just use clipBoardManager = new ClipBoardManager(clipboard, importFormatReader) .
|
||||
try { | ||||
field.setAccessible(true); | ||||
|
||||
Field modifiersField = Field.class.getDeclaredField("modifiers"); | ||||
modifiersField.setAccessible(true); | ||||
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); | ||||
|
||||
field.set(ClipBoardManager.class, clipboard); | ||||
} finally { | ||||
field.setAccessible(false); | ||||
} | ||||
|
||||
content = mock(Transferable.class); | ||||
when(clipboard.getContents(ArgumentMatchers.any())).thenReturn(content); | ||||
} | ||||
|
||||
@Test | ||||
public void extractBibEntriesFromClipboardParsesStringFlavor() throws Exception { | ||||
BibEntry expected = new BibEntry(); | ||||
expected.setType("article"); | ||||
expected.setCiteKey("canh05"); | ||||
expected.setField("author", "Crowston, K. and Annabi, H."); | ||||
expected.setField("title", "Title A"); | ||||
|
||||
when(content.isDataFlavorSupported(TransferableBibtexEntry.ENTRY_FLAVOR)).thenReturn(false); | ||||
when(content.isDataFlavorSupported(DataFlavor.stringFlavor)).thenReturn(true); | ||||
String data = "@article{canh05, author = {Crowston, K. and Annabi, H.},\n" + " title = {Title A}}\n"; | ||||
when(content.getTransferData(DataFlavor.stringFlavor)).thenReturn(data); | ||||
when(importFormatReader.importUnknownFormatFromString(data)).thenReturn(new UnknownFormatImport("abc", new ParserResult(Arrays.asList(expected)))); | ||||
|
||||
List<BibEntry> actual = clipBoardManager.extractBibEntriesFromClipboard(); | ||||
|
||||
assertEquals(Arrays.asList(expected), actual); | ||||
} | ||||
|
||||
@Test | ||||
public void extractBibEntriesFromClipboardReturnsEmptyIfStringparsingFailed() throws Exception { | ||||
when(content.isDataFlavorSupported(TransferableBibtexEntry.ENTRY_FLAVOR)).thenReturn(false); | ||||
when(content.isDataFlavorSupported(DataFlavor.stringFlavor)).thenReturn(true); | ||||
when(content.getTransferData(DataFlavor.stringFlavor)).thenReturn("testData"); | ||||
when(importFormatReader.importUnknownFormatFromString("testData")).thenThrow(new ImportException("")); | ||||
|
||||
List<BibEntry> actual = clipBoardManager.extractBibEntriesFromClipboard(); | ||||
|
||||
assertEquals(Arrays.asList(), actual); | ||||
} | ||||
|
||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
import java.net.URISyntaxException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.ArrayList; | ||
|
@@ -28,7 +29,6 @@ public class ImportFormatReaderIntegrationTest { | |
public final String format; | ||
private final Path file; | ||
|
||
|
||
public ImportFormatReaderIntegrationTest(String resource, String format, int count) throws URISyntaxException { | ||
this.format = format; | ||
this.count = count; | ||
|
@@ -55,6 +55,20 @@ public void testImportFormatFromFile() throws Exception { | |
assertEquals(count, reader.importFromFile(format, file).getDatabase().getEntries().size()); | ||
} | ||
|
||
@Test | ||
public void testImportUnknownFormatFromString() throws Exception { | ||
String data = new String(Files.readAllBytes(file), StandardCharsets.UTF_8); | ||
try { | ||
assertEquals(count, reader.importUnknownFormatFromString(data).parserResult.getDatabase().getEntries().size()); | ||
} catch (ImportException e) { | ||
// msbib test file does not contain an author | ||
if ("msbib".equals(format)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this special treatment necessary for the import from a string, but not when the same content is imported from a file? |
||
return; | ||
} | ||
throw e; | ||
} | ||
} | ||
|
||
@Parameterized.Parameters(name = "{index}: {1}") | ||
public static Collection<Object[]> importFormats() { | ||
Collection<Object[]> result = new ArrayList<>(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can see, this is mostly a copy of
importUnknownFormat(Path filePath)
and I would like to have a solution without code duplication. Maybe something along the following lines works?It's a bit ugly. Do you have a better idea to reduce the code duplication?