Skip to content

Commit 7317c2e

Browse files
jeanprbtdependabot[bot]Siedlerchrshawn-jjkoppor
authored
Added option to export in CFF (Citation File Format) (#10917)
* issue #10661 - feat: added option to export in cff * issue #10661 - feat: updated CHANGELOG.md * issue #10661 - feat: added date field to cff export * issue #10661 - fix: fixed checkstyle errors * Bump org.apache.lucene:lucene-queries from 9.9.1 to 9.10.0 (#10920) * Bump org.apache.lucene:lucene-queries from 9.9.1 to 9.10.0 Bumps org.apache.lucene:lucene-queries from 9.9.1 to 9.10.0. --- updated-dependencies: - dependency-name: org.apache.lucene:lucene-queries dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * introduce var for lucene --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Siedlerchr <siedlerkiller@gmail.com> * Fix for delete entries should ask user (#10591) * Implemented the feature that deleting files which linked to selected entries when user select deletion, and keeping files unchanged when user select cut * The following features are implemented: 1.Initializes a pop-up dialog box to confirm whether the user wants to delete attached files from selected entry. 2.Keep track of user preference: if the user prefers always delete attached files, delete the files without displaying the dialog box. 3. Add preference options in File>Preference>Linked Files>Attached files so that users can manage preferences * update CHANGELOG.md * Add language keys to english language file * restore files in src/main/resources/csl-locales and src/main/resources/csl-styles * Removed unnecessary comments and finxed some requested changes. Added new features: 1. When deleting attached files, the name of files to be deleted will be displayed. 2. Solved the access error caused by repeated deletion of files when one file is attached to multiple entries. * Add language keys to english language file * Modify language keys to english language file * made deleteFileFromDisk method static * update comment of method deleteFileFromDisk * fixed coding styles * restored unexpected code changes * fix logic * try null * todo * Unify dialogs that confirmation deleting files * Get around LinkedFile in LIbraryTab, Encapsulate LinkedFile into LinkedFileViewModel * fix style * restore files * Unified the different dialogs when deleting entries, removerd unnecessary dialogs * fix csl-styles * try to fix csl-styles * try to fix csl-styles again * try to fix csl-styles again 2 * try to fix csl-styles again 3 * Update prompts in en.properties * New features - Add to Trash - Group file-related language strings together * Fix architecture tests * Introduce list of files to delete * Streamline 1 vs. many files * Fix openRewrite * Discard changes to src/test/resources/org/jabref/logic/search/test-library-with-attached-files.bib * Adapt true/false logic according to expectations * Add "Trash" to CHANGELOG.md * Fix localization * Fix JabRef_en.properties * Add some debug statements * Fix preferences * Separate log entries by empty line * More refined dialog --------- Co-authored-by: Siedlerchr <siedlerkiller@gmail.com> Co-authored-by: Oliver Kopp <kopp.dev@gmail.com> Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> * Fix variable name * Fix duplicate check/merge entries dialog not triggered on import from… (#10914) * Fix duplicate check/merge entries dialog not triggered on import from browser Refs #5858 * changelog * remove double duplicate check * remove l10n * add icon, downloading is also handled in import entries * changelog * fix l10n * Update JabRef_en.properties * Update ImportEntriesDialog.java --------- Co-authored-by: Oliver Kopp <kopp.dev@gmail.com> * issue #10661 - fix: fixed PR review comments Made a class comment in CffDate.java Replaced System.lineseparator() with OS.NEWLINE in CffDate.java Added a final newline in cff.layout file --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Siedlerchr <siedlerkiller@gmail.com> Co-authored-by: shawn-jj <134609685+shawn-jj@users.noreply.github.com> Co-authored-by: Oliver Kopp <kopp.dev@gmail.com> Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com>
1 parent 27f3ed6 commit 7317c2e

File tree

8 files changed

+321
-0
lines changed

8 files changed

+321
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
2323
- When pasting HTML into the abstract or a comment field, the hypertext is automatically converted to Markdown. [#10558](https://github.com/JabRef/jabref/issues/10558)
2424
- We added the possibility to redownload files that had been present but are no longer in the specified location. [#10848](https://github.com/JabRef/jabref/issues/10848)
2525
- We added the citation key pattern `[camelN]`. Equivalent to the first N words of the `[camel]` pattern.
26+
- We added ability to export in CFF (Citation File Format) [#10661](https://github.com/JabRef/jabref/issues/10661).
2627

2728
### Changed
2829

src/main/java/org/jabref/logic/exporter/ExporterFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public static ExporterFactory create(PreferencesService preferencesService,
5555
exporters.add(new TemplateExporter("MIS Quarterly", "misq", "misq", "misq", StandardFileType.RTF, layoutPreferences, saveOrder));
5656
exporters.add(new TemplateExporter("CSL YAML", "yaml", "yaml", null, StandardFileType.YAML, layoutPreferences, saveOrder, BlankLineBehaviour.DELETE_BLANKS));
5757
exporters.add(new TemplateExporter("Hayagriva YAML", "hayagrivayaml", "hayagrivayaml", null, StandardFileType.YAML, layoutPreferences, saveOrder, BlankLineBehaviour.DELETE_BLANKS));
58+
exporters.add(new TemplateExporter("CFF", "cff", "cff", null, StandardFileType.CFF, layoutPreferences, saveOrder, BlankLineBehaviour.DELETE_BLANKS));
5859
exporters.add(new OpenOfficeDocumentCreator());
5960
exporters.add(new OpenDocumentSpreadsheetCreator());
6061
exporters.add(new MSBibExporter());

src/main/java/org/jabref/logic/layout/LayoutEntry.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import org.jabref.logic.layout.format.AuthorOrgSci;
3636
import org.jabref.logic.layout.format.Authors;
3737
import org.jabref.logic.layout.format.CSLType;
38+
import org.jabref.logic.layout.format.CffDate;
39+
import org.jabref.logic.layout.format.CffType;
3840
import org.jabref.logic.layout.format.CompositeFormat;
3941
import org.jabref.logic.layout.format.CreateBibORDFAuthors;
4042
import org.jabref.logic.layout.format.CreateDocBook4Authors;
@@ -486,6 +488,8 @@ private LayoutFormatter getLayoutFormatterByName(String name) {
486488
case "ShortMonth" -> new ShortMonthFormatter();
487489
case "ReplaceWithEscapedDoubleQuotes" -> new ReplaceWithEscapedDoubleQuotes();
488490
case "HayagrivaType" -> new HayagrivaType();
491+
case "CffType" -> new CffType();
492+
case "CffDate" -> new CffDate();
489493
default -> null;
490494
};
491495
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.jabref.logic.layout.format;
2+
3+
import java.time.LocalDate;
4+
import java.time.Year;
5+
import java.time.YearMonth;
6+
import java.time.format.DateTimeFormatter;
7+
import java.time.format.DateTimeParseException;
8+
9+
import org.jabref.logic.layout.LayoutFormatter;
10+
import org.jabref.logic.util.OS;
11+
12+
/**
13+
* This class is used to parse dates for CFF exports. Since we do not know if the input String contains
14+
* year, month and day, we must go through all these cases to return the best CFF format possible.
15+
* Different cases are stated below.
16+
* <p>
17+
* Year, Month and Day contained => preferred-citation:
18+
* date-released: yyyy-mm-dd
19+
* <p>
20+
* Year and Month contained => preferred-citation
21+
* ...
22+
* month: mm
23+
* year: yyyy
24+
* <p>
25+
* Year contained => preferred-citation:
26+
* ...
27+
* year: yyyy
28+
* <p>
29+
* Poorly formatted => preferred-citation:
30+
* ...
31+
* issue-date: text-as-is
32+
*/
33+
public class CffDate implements LayoutFormatter {
34+
@Override
35+
public String format(String fieldText) {
36+
StringBuilder builder = new StringBuilder();
37+
String formatString = "yyyy-MM-dd";
38+
try {
39+
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatString);
40+
LocalDate date = LocalDate.parse(fieldText, DateTimeFormatter.ISO_LOCAL_DATE);
41+
builder.append("date-released: ");
42+
builder.append(date.format(formatter));
43+
} catch (DateTimeParseException e) {
44+
try {
45+
formatString = "yyyy-MM";
46+
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatString);
47+
YearMonth yearMonth = YearMonth.parse(fieldText, formatter);
48+
int month = yearMonth.getMonth().getValue();
49+
int year = yearMonth.getYear();
50+
builder.append("month: ");
51+
builder.append(month);
52+
builder.append(OS.NEWLINE);
53+
builder.append(" year: "); // Account for indent since we are in `preferred-citation` indentation block
54+
builder.append(year);
55+
} catch (DateTimeParseException f) {
56+
try {
57+
formatString = "yyyy";
58+
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatString);
59+
int year = Year.parse(fieldText, formatter).getValue();
60+
builder.append("year: ");
61+
builder.append(year);
62+
} catch (DateTimeParseException g) {
63+
builder.append("issue-date: ");
64+
builder.append(fieldText);
65+
}
66+
}
67+
}
68+
return builder.toString();
69+
}
70+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.jabref.logic.layout.format;
2+
3+
import org.jabref.logic.layout.LayoutFormatter;
4+
import org.jabref.model.entry.types.StandardEntryType;
5+
6+
public class CffType implements LayoutFormatter {
7+
@Override
8+
public String format(String value) {
9+
return switch (StandardEntryType.valueOf(value)) {
10+
case Article, Conference -> "article";
11+
case Book -> "book";
12+
case Booklet -> "pamphlet";
13+
case InProceedings -> "conference-paper";
14+
case Proceedings -> "proceedings";
15+
case Misc -> "misc";
16+
case Manual -> "manual";
17+
case Software -> "software";
18+
case Report, TechReport -> "report";
19+
case Unpublished -> "unpublished";
20+
default -> "generic";
21+
};
22+
}
23+
}
24+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
cff-version: 1.2.0
2+
message: "If you use this, please cite the work from preferred-citation."
3+
authors:
4+
- name: \format[Default(No author specified.)]{\author}
5+
title: \format[Default(No title specified.)]{\title}
6+
preferred-citation:
7+
type: \format[CffType, Default(generic)]{\entrytype}
8+
authors:
9+
- name: \format[Default(No author specified.)]{\author}
10+
title: \format[Default(No title specified.)]{\title}
11+
\begin{date}
12+
\format[CffDate]{\date}
13+
\end{date}
14+
\begin{abstract} abstract: \abstract\end{abstract}
15+
\begin{doi} doi: \doi\end{doi}
16+
\begin{volume} volume: \volume\end{volume}
17+
\begin{url} url: "\url"\end{url}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package org.jabref.logic.exporter;
2+
3+
import java.nio.file.Files;
4+
import java.nio.file.Path;
5+
import java.util.Collections;
6+
import java.util.List;
7+
8+
import org.jabref.logic.layout.LayoutFormatterPreferences;
9+
import org.jabref.logic.util.StandardFileType;
10+
import org.jabref.model.database.BibDatabaseContext;
11+
import org.jabref.model.entry.BibEntry;
12+
import org.jabref.model.entry.field.StandardField;
13+
import org.jabref.model.entry.types.StandardEntryType;
14+
import org.jabref.model.metadata.SaveOrder;
15+
16+
import org.junit.jupiter.api.BeforeAll;
17+
import org.junit.jupiter.api.Test;
18+
import org.junit.jupiter.api.io.TempDir;
19+
import org.mockito.Answers;
20+
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
import static org.mockito.Mockito.mock;
23+
24+
public class CffExporterTest {
25+
26+
private static Exporter cffExporter;
27+
private static BibDatabaseContext databaseContext;
28+
29+
@BeforeAll
30+
static void setUp() {
31+
cffExporter = new TemplateExporter(
32+
"CFF",
33+
"cff",
34+
"cff",
35+
null,
36+
StandardFileType.CFF,
37+
mock(LayoutFormatterPreferences.class, Answers.RETURNS_DEEP_STUBS),
38+
SaveOrder.getDefaultSaveOrder(),
39+
BlankLineBehaviour.DELETE_BLANKS);
40+
41+
databaseContext = new BibDatabaseContext();
42+
}
43+
44+
@Test
45+
public final void exportForNoEntriesWritesNothing(@TempDir Path tempFile) throws Exception {
46+
Path file = tempFile.resolve("ThisIsARandomlyNamedFile");
47+
Files.createFile(file);
48+
cffExporter.export(databaseContext, tempFile, Collections.emptyList());
49+
assertEquals(Collections.emptyList(), Files.readAllLines(file));
50+
}
51+
52+
@Test
53+
public final void exportsCorrectContent(@TempDir Path tempFile) throws Exception {
54+
BibEntry entry = new BibEntry(StandardEntryType.Article)
55+
.withCitationKey("test")
56+
.withField(StandardField.AUTHOR, "Test Author")
57+
.withField(StandardField.TITLE, "Test Title")
58+
.withField(StandardField.URL, "http://example.com");
59+
60+
Path file = tempFile.resolve("RandomFileName");
61+
Files.createFile(file);
62+
cffExporter.export(databaseContext, file, Collections.singletonList(entry));
63+
64+
List<String> expected = List.of(
65+
"cff-version: 1.2.0",
66+
"message: \"If you use this, please cite the work from preferred-citation.\"",
67+
"authors:",
68+
" - name: Test Author",
69+
"title: Test Title",
70+
"preferred-citation:",
71+
" type: article",
72+
" authors:",
73+
" - name: Test Author",
74+
" title: Test Title",
75+
" url: \"http://example.com\"");
76+
77+
assertEquals(expected, Files.readAllLines(file));
78+
}
79+
80+
@Test
81+
public final void usesCorrectType(@TempDir Path tempFile) throws Exception {
82+
BibEntry entry = new BibEntry(StandardEntryType.InProceedings)
83+
.withCitationKey("test")
84+
.withField(StandardField.AUTHOR, "Test Author")
85+
.withField(StandardField.TITLE, "Test Title")
86+
.withField(StandardField.DOI, "random_doi_value");
87+
88+
Path file = tempFile.resolve("RandomFileName");
89+
Files.createFile(file);
90+
cffExporter.export(databaseContext, file, Collections.singletonList(entry));
91+
92+
List<String> expected = List.of(
93+
"cff-version: 1.2.0",
94+
"message: \"If you use this, please cite the work from preferred-citation.\"",
95+
"authors:",
96+
" - name: Test Author",
97+
"title: Test Title",
98+
"preferred-citation:",
99+
" type: conference-paper",
100+
" authors:",
101+
" - name: Test Author",
102+
" title: Test Title",
103+
" doi: random_doi_value");
104+
105+
assertEquals(expected, Files.readAllLines(file));
106+
}
107+
108+
@Test
109+
public final void usesCorrectDefaultValues(@TempDir Path tempFile) throws Exception {
110+
BibEntry entry = new BibEntry(StandardEntryType.Thesis)
111+
.withCitationKey("test");
112+
113+
Path file = tempFile.resolve("RandomFileName");
114+
Files.createFile(file);
115+
cffExporter.export(databaseContext, file, Collections.singletonList(entry));
116+
117+
List<String> expected = List.of(
118+
"cff-version: 1.2.0",
119+
"message: \"If you use this, please cite the work from preferred-citation.\"",
120+
"authors:",
121+
" - name: No author specified.",
122+
"title: No title specified.",
123+
"preferred-citation:",
124+
" type: generic",
125+
" authors:",
126+
" - name: No author specified.",
127+
" title: No title specified.");
128+
129+
assertEquals(expected, Files.readAllLines(file));
130+
}
131+
132+
@Test
133+
void passesModifiedCharset(@TempDir Path tempFile) throws Exception {
134+
BibEntry entry = new BibEntry(StandardEntryType.Article)
135+
.withCitationKey("test")
136+
.withField(StandardField.AUTHOR, "谷崎 潤一郎")
137+
.withField(StandardField.TITLE, "細雪")
138+
.withField(StandardField.URL, "http://example.com");
139+
140+
Path file = tempFile.resolve("RandomFileName");
141+
Files.createFile(file);
142+
cffExporter.export(databaseContext, file, Collections.singletonList(entry));
143+
144+
List<String> expected = List.of(
145+
"cff-version: 1.2.0",
146+
"message: \"If you use this, please cite the work from preferred-citation.\"",
147+
"authors:",
148+
" - name: 谷崎 潤一郎",
149+
"title: 細雪",
150+
"preferred-citation:",
151+
" type: article",
152+
" authors:",
153+
" - name: 谷崎 潤一郎",
154+
" title: 細雪",
155+
" url: \"http://example.com\"");
156+
157+
assertEquals(expected, Files.readAllLines(file));
158+
}
159+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.jabref.logic.layout.format;
2+
3+
import org.jabref.logic.layout.LayoutFormatter;
4+
import org.jabref.logic.util.OS;
5+
6+
import org.junit.jupiter.api.BeforeEach;
7+
import org.junit.jupiter.api.Test;
8+
9+
import static org.junit.jupiter.api.Assertions.assertEquals;
10+
11+
public class CffDateTest {
12+
13+
private LayoutFormatter formatter;
14+
private String newLine;
15+
16+
@BeforeEach
17+
public void setUp() {
18+
formatter = new CffDate();
19+
newLine = OS.NEWLINE;
20+
}
21+
22+
@Test
23+
public void dayMonthYear() {
24+
String expected = "date-released: 2003-11-06";
25+
assertEquals(expected, formatter.format("2003-11-06"));
26+
}
27+
28+
@Test
29+
public void monthYear() {
30+
String expected = "month: 7" + newLine + " " + "year: 2016";
31+
assertEquals(expected, formatter.format("2016-07"));
32+
}
33+
34+
@Test
35+
public void year() {
36+
String expected = "year: 2021";
37+
assertEquals(expected, formatter.format("2021"));
38+
}
39+
40+
@Test
41+
public void poorlyFormatted() {
42+
String expected = "issue-date: -2023";
43+
assertEquals(expected, formatter.format("-2023"));
44+
}
45+
}

0 commit comments

Comments
 (0)