Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
01fdc3e
Refactor CitationStyleGeneratorTest
subhramit Aug 16, 2024
833be28
Add test: [StAX] Parse title, isNumericStyle
subhramit Aug 16, 2024
1bf6b59
Add CSLFormatUtils
subhramit Aug 16, 2024
3c0bc52
Refactor CSLCitationOOAdapter and add JavaDoc
subhramit Aug 16, 2024
262287a
Add test for citeproc DIN 1505-2
subhramit Aug 16, 2024
3f87126
Better method names, more javadoc
subhramit Aug 16, 2024
70a8e47
Add tests for CSLFormatUtils
subhramit Aug 16, 2024
be680d3
Add javadoc for adapter
subhramit Aug 16, 2024
b9b915f
Fix locales
subhramit Aug 16, 2024
44773dc
Fix submodules for styles
subhramit Aug 16, 2024
c143782
OpenRewrite
subhramit Aug 16, 2024
168ac64
Fix submodules for styles
subhramit Aug 16, 2024
9848d78
Fix locales
subhramit Aug 16, 2024
cc24c98
Rename test method
subhramit Aug 16, 2024
8d81fbb
Change order of methods in mark manager
subhramit Aug 16, 2024
20e7947
Merge branch 'main' into csl-tests
subhramit Aug 16, 2024
dd56e89
Fix submodules for styles
subhramit Aug 16, 2024
5840564
Fix locales
subhramit Aug 16, 2024
5c53a15
Merge remote-tracking branch 'upstream/main' into csl-tests
subhramit Aug 16, 2024
95041f2
Merge branch 'main' into csl-tests
subhramit Aug 16, 2024
f81daf0
Merge branch 'csl-tests' of https://github.com/JabRef/jabref into csl…
subhramit Aug 16, 2024
8a23b93
Merge remote-tracking branch 'upstream/main' into csl-tests
subhramit Aug 16, 2024
cd2539c
Disable test
subhramit Aug 16, 2024
02fbe4e
Merge remote-tracking branch 'upstream/main' into csl-tests
subhramit Aug 19, 2024
4a90528
Merge branch 'main' into csl-tests
subhramit Aug 20, 2024
84759e0
Merge branch 'csl-tests' of https://github.com/JabRef/jabref into csl…
subhramit Aug 20, 2024
4b25932
Review actions - I
subhramit Aug 20, 2024
cf51468
Better javadoc for disabled test
subhramit Aug 20, 2024
0255611
Remove "public" as per best practices
subhramit Aug 20, 2024
0c7e84a
Merge branch 'main' into csl-tests
subhramit Aug 20, 2024
71ab2cd
Merge branch 'main' into csl-tests
subhramit Aug 20, 2024
e132bdf
Merge remote-tracking branch 'upstream/main' into csl-tests
subhramit Aug 21, 2024
30b8103
Merge remote-tracking branch 'upstream/main' into csl-tests
subhramit Aug 21, 2024
3962402
Review changes [3]
subhramit Aug 21, 2024
ff3cb2a
Merge branch 'csl-tests' of https://github.com/JabRef/jabref into csl…
subhramit Aug 21, 2024
f77c35e
Swap arguments for CitationStyleTest
subhramit Aug 21, 2024
08a712b
Merge remote-tracking branch 'upstream/main' into csl-tests
subhramit Aug 21, 2024
2a611de
Add comment to @Disabled
subhramit Aug 21, 2024
38a58d2
Merge branch 'main' into csl-tests
subhramit Aug 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ private static Optional<CitationStyle> createCitationStyleFromSource(final Input
public record StyleInfo(String title, boolean isNumericStyle) {
}

private static Optional<StyleInfo> parseStyleInfo(String filename, String content) {
public static Optional<StyleInfo> parseStyleInfo(String filename, String content) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why public? It is used only in this class?

image

If you will need it for testing, make it package private and annotate it with @VisibleForTesting

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

FACTORY.setProperty(XMLInputFactory.IS_COALESCING, true);

try {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package org.jabref.logic.openoffice.oocsltext;

import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jabref.logic.citationkeypattern.BracketedPattern;
import org.jabref.logic.citationstyle.CitationStyleOutputFormat;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.AuthorList;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.openoffice.ootext.OOText;
import org.jabref.model.openoffice.ootext.OOTextIntoOO;

import com.sun.star.text.XTextCursor;
import com.sun.star.text.XTextDocument;
import org.apache.commons.text.StringEscapeUtils;

/**
* Contains utility constants and methods for processing of CSL citations as generated by methods of <a href="https://github.com/michel-kraemer/citeproc-javaciteproc-java">citeproc-java</a> ({@link org.jabref.logic.citationstyle.CitationStyleGenerator}).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The link https://github.com/michel-kraemer/citeproc-javaciteproc-java leads to a 404. I suppose you meant https://github.com/michel-kraemer/citeproc-java?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thanks for pointing this out!
Fixed.

* <p>These methods are used in {@link CSLCitationOOAdapter} which inserts CSL citation text into an OO document.</p>
*/
public class CSLFormatUtils {

// TODO: These are static final fields right now, should add the functionality to let user select these and store them in preferences.
public static final String DEFAULT_BIBLIOGRAPHY_TITLE = "References";
public static final String DEFAULT_BIBLIOGRAPHY_HEADER_PARAGRAPH_FORMAT = "Heading 2";
public static final CitationStyleOutputFormat OUTPUT_FORMAT = CitationStyleOutputFormat.HTML;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, this should be separated by a newline from the constant before. Reason: This one should not be configurable, should it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, separated.

private static final Pattern YEAR_IN_CITATION_PATTERN = Pattern.compile("(.)(.*), (\\d{4}.*)");

/**
* Transforms provided HTML into a format that can be fully parsed and inserted into an OO document.
* Context: The HTML produced by {@link org.jabref.logic.citationstyle.CitationStyleGenerator#generateCitation(List, String, CitationStyleOutputFormat, BibDatabaseContext, BibEntryTypesManager) generateCitation} or {@link org.jabref.logic.citationstyle.CitationStyleGenerator#generateInText(List, String, CitationStyleOutputFormat, BibDatabaseContext, BibEntryTypesManager) generateInText} is not directly (completely) parsable by by {@link OOTextIntoOO#write(XTextDocument, XTextCursor, OOText) write}.
* For more details, read the documentation for the {@link OOTextIntoOO} class.
* <a href="https://devdocs.jabref.org/code-howtos/openoffice/code-reorganization.html">Additional Information</a>.
*
* @param html The HTML string to be transformed into OO-write ready HTML.
* @return The formatted html string.
*/
public static String transformHTML(String html) {
// Initial clean up of escaped characters
html = StringEscapeUtils.unescapeHtml4(html);

// Handle margins (spaces between citation number and text)
html = html.replaceAll("<div class=\"csl-left-margin\">(.*?)</div><div class=\"csl-right-inline\">(.*?)</div>", "$1 $2");

// Remove unsupported tags
html = html.replaceAll("<div[^>]*>", "");
html = html.replace("</div>", "");

// Remove unsupported links
html = html.replaceAll("<a[^>]*>", "");
html = html.replace("</a>", "");

// Replace span tags with inline styles for bold
html = html.replaceAll("<span style=\"font-weight: ?bold;?\">(.*?)</span>", "<b>$1</b>");

// Replace span tags with inline styles for italic
html = html.replaceAll("<span style=\"font-style: ?italic;?\">(.*?)</span>", "<i>$1</i>");

// Replace span tags with inline styles for underline
html = html.replaceAll("<span style=\"text-decoration: ?underline;?\">(.*?)</span>", "<u>$1</u>");

html = html.replaceAll("<span style=\"font-variant: ?small-caps;?\">(.*?)</span>", "<smallcaps>$1</smallcaps>");

// Clean up any remaining span tags
html = html.replaceAll("</?span[^>]*>", "");

return html;
}

/**
* Alphanumeric citations are not natively supported by citeproc-java (see {@link org.jabref.logic.citationstyle.CitationStyleGenerator#generateInText(List, String, CitationStyleOutputFormat, BibDatabaseContext, BibEntryTypesManager) generateInText}).
* Thus, we manually format a citation to produce its alphanumeric form.
*
* @param entries the list of entries for which the alphanumeric citation is to be generated.
* @return the alphanumeric citation (for a single entry or a group of entries).
*/
public static String generateAlphanumericCitation(List<BibEntry> entries, BibDatabaseContext bibDatabaseContext) {
StringBuilder citation = new StringBuilder("[");
for (int i = 0; i < entries.size(); i++) {
BibEntry entry = entries.get(i);
Optional<String> author = entry.getResolvedFieldOrAlias(StandardField.AUTHOR, bibDatabaseContext.getDatabase());
Optional<String> year = entry.getResolvedFieldOrAlias(StandardField.YEAR, bibDatabaseContext.getDatabase());

if (author.isPresent() && year.isPresent()) {
AuthorList authorList = AuthorList.parse(author.get());
String alphaKey = BracketedPattern.authorsAlpha(authorList);

// Extract last two digits of the year
String shortYear = year.get().length() >= 2 ?
year.get().substring(year.get().length() - 2) :
year.get();

citation.append(alphaKey).append(shortYear);
} else {
citation.append(entry.getCitationKey().orElse(""));
}

if (i < entries.size() - 1) {
citation.append("; ");
}
}
citation.append("]");
return citation.toString();
}

/**
* Method to update citation number of a bibliographic entry (to be inserted in the list of references).
* By default, citeproc-java ({@link org.jabref.logic.citationstyle.CitationStyleGenerator#generateCitation(List, String, CitationStyleOutputFormat, BibDatabaseContext, BibEntryTypesManager) generateCitation} always start the numbering of a list of citations with "1".
* If a citation doesn't correspond to the first cited entry, the number should be changed to the relevant current citation number.
* If an entries has been cited before, the colder number should be reused.
* The number can be enclosed in different formats, such as "1", "1.", "1)", "(1)" or "[1]".
* <p>
* <b>Precondition:</b> Use ONLY with numeric citation styles.</p>
*
* @param citation the numeric citation with an unresolved number.
* @param currentNumber the correct number to update the citation with.
* @return the bibliographic citation with resolved number.
*/
public static String updateSingleBibliographyNumber(String citation, int currentNumber) {
Pattern pattern = Pattern.compile("(\\[|\\()?(\\d+)(\\]|\\))?(\\.)?\\s*");
Matcher matcher = pattern.matcher(citation);
StringBuilder sb = new StringBuilder();
boolean numberReplaced = false;

while (matcher.find()) {
if (!numberReplaced) {
String prefix = matcher.group(1) != null ? matcher.group(1) : "";
String suffix = matcher.group(3) != null ? matcher.group(3) : "";
String dot = matcher.group(4) != null ? "." : "";
String space = matcher.group().endsWith(" ") ? " " : "";

String replacement = prefix + currentNumber + suffix + dot + space;

matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement));
numberReplaced = true;
} else {
matcher.appendReplacement(sb, matcher.group());
}
}
matcher.appendTail(sb);
return sb.toString();
}

/**
* Extracts year from a citation having single or multiple entries, for the purpose of using in in-text citations.
*
* @param formattedCitation the citation cleaned up and formatted using {@link CSLFormatUtils#transformHTML transformHTML}.
*/
public static String changeToInText(String formattedCitation) {
Matcher matcher = YEAR_IN_CITATION_PATTERN.matcher(formattedCitation);
if (matcher.find()) {
return matcher.group(2) + " " + matcher.group(1) + matcher.group(3);
}
return formattedCitation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ public CSLReferenceMarkManager(XTextDocument document) {
this.citationKeyToNumber = new HashMap<>();
}

public CSLReferenceMark createReferenceMark(BibEntry entry) throws Exception {
String citationKey = entry.getCitationKey().orElse(CUID.randomCUID2(8).toString());
int citationNumber = getCitationNumber(citationKey);
CSLReferenceMark referenceMark = CSLReferenceMark.of(citationKey, citationNumber, factory);
addMark(referenceMark);
return referenceMark;
}

public void addMark(CSLReferenceMark mark) {
marksByName.put(mark.getName(), mark);
idsByMark.put(mark, marksByID.size());
marksByID.add(mark);
updateCitationInfo(mark.getName());
}

public void readExistingMarks() throws WrappedTargetException, NoSuchElementException {
XReferenceMarksSupplier supplier = UnoRuntime.queryInterface(XReferenceMarksSupplier.class, document);
XNameAccess marks = supplier.getReferenceMarks();
Expand Down Expand Up @@ -72,26 +87,11 @@ private void updateCitationInfo(String name) {
}
}

public void addMark(CSLReferenceMark mark) {
marksByName.put(mark.getName(), mark);
idsByMark.put(mark, marksByID.size());
marksByID.add(mark);
updateCitationInfo(mark.getName());
public boolean hasCitationForKey(String citationKey) {
return citationKeyToNumber.containsKey(citationKey);
}

public int getCitationNumber(String citationKey) {
return citationKeyToNumber.computeIfAbsent(citationKey, k -> ++highestCitationNumber);
}

public CSLReferenceMark createReferenceMark(BibEntry entry) throws Exception {
String citationKey = entry.getCitationKey().orElse(CUID.randomCUID2(8).toString());
int citationNumber = getCitationNumber(citationKey);
CSLReferenceMark referenceMark = CSLReferenceMark.of(citationKey, citationNumber, factory);
addMark(referenceMark);
return referenceMark;
}

public boolean hasCitationForKey(String citationKey) {
return citationKeyToNumber.containsKey(citationKey);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@

class CitationStyleGeneratorTest {

private final BibEntry testEntry = TestEntry.getTestEntry();
private final BibDatabaseContext context = new BibDatabaseContext(new BibDatabase(List.of(testEntry)));
private final BibEntryTypesManager bibEntryTypesManager = new BibEntryTypesManager();
private final List<CitationStyle> styleList = CitationStyle.discoverCitationStyles();

@Test
void aCMCitation() {
BibDatabaseContext context = new BibDatabaseContext(new BibDatabase(List.of(TestEntry.getTestEntry())));
context.setMode(BibDatabaseMode.BIBLATEX);
List<CitationStyle> styleList = CitationStyle.discoverCitationStyles();
CitationStyle style = styleList.stream().filter(e -> "ACM SIGGRAPH".equals(e.getTitle())).findAny().orElse(null);
String citation = CitationStyleGenerator.generateCitation(List.of(TestEntry.getTestEntry()), style.getSource(), CitationStyleOutputFormat.HTML, context, new BibEntryTypesManager()).getFirst();
String citation = CitationStyleGenerator.generateCitation(List.of(testEntry), style.getSource(), CitationStyleOutputFormat.HTML, context, bibEntryTypesManager).getFirst();

// if the acm-siggraph.csl citation style changes this has to be modified
String expected = " <div class=\"csl-entry\">"
Expand All @@ -43,11 +44,9 @@ void aCMCitation() {

@Test
void aPACitation() {
BibDatabaseContext context = new BibDatabaseContext(new BibDatabase(List.of(TestEntry.getTestEntry())));
context.setMode(BibDatabaseMode.BIBLATEX);
List<CitationStyle> styleList = CitationStyle.discoverCitationStyles();
CitationStyle style = styleList.stream().filter(e -> "American Psychological Association 7th edition".equals(e.getTitle())).findAny().orElse(null);
String citation = CitationStyleGenerator.generateCitation(List.of(TestEntry.getTestEntry()), style.getSource(), CitationStyleOutputFormat.HTML, context, new BibEntryTypesManager()).getFirst();
String citation = CitationStyleGenerator.generateCitation(List.of(testEntry), style.getSource(), CitationStyleOutputFormat.HTML, context, bibEntryTypesManager).getFirst();

// if the apa-7th-citation.csl citation style changes this has to be modified
String expected = " <div class=\"csl-entry\">"
Expand All @@ -58,6 +57,22 @@ void aPACitation() {
assertEquals(expected, citation);
}

/**
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just mark the test @disabled

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is IMHO helpful. For someone diving into it 1 year later.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marked disabled and kept the comment.

* The below test, as of now, fails due to citeproc-java returning an empty citation.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to be shorter:

Suggested change
* The below test, as of now, fails due to citeproc-java returning an empty citation.
* Fails due to citeproc-java returning an empty citation.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

* Alphanumeric citations are thus, currently manually generated by formatting (see {@link org.jabref.logic.openoffice.oocsltext.CSLFormatUtils#generateAlphanumericCitation(List, BibDatabaseContext) generateAlphaNumericCitation}).
*/
// @Test
// void din1502AlphanumericInTextCitation() throws IOException {
// context.setMode(BibDatabaseMode.BIBLATEX);
// CitationStyle style = styleList.stream().filter(e -> "DIN 1505-2 (alphanumeric, Deutsch) - standard superseded by ISO-690".equals(e.getTitle())).findAny().orElse(null);
// Citation citation = CitationStyleGenerator.generateInText(List.of(testEntry), style.getSource(), CitationStyleOutputFormat.HTML, context, bibEntryTypesManager);
// String inTextCitationText = citation.getText();
//
// String expected = "[Smit2016]";
//
// assertEquals(expected, inTextCitationText);
// }

@Test
void ignoreNewLine() {
BibEntry entry = new BibEntry();
Expand Down Expand Up @@ -97,23 +112,21 @@ void htmlFormat() {
" <div class=\"csl-left-margin\">[1]</div><div class=\"csl-right-inline\">B. Smith, B. Jones, and J. Williams, &ldquo;Title of the test entry,&rdquo; <span style=\"font-style: italic\">BibTeX Journal</span>, vol. 34, no. 3, pp. 45&ndash;67, Jul. 2016, doi: 10.1001/bla.blubb.</div>\n" +
" </div>\n";

BibEntry entry = TestEntry.getTestEntry();
String style = CitationStyle.getDefault().getSource();
CitationStyleOutputFormat format = CitationStyleOutputFormat.HTML;

String actualCitation = CitationStyleGenerator.generateCitation(List.of(entry), style, format, new BibDatabaseContext(), bibEntryTypesManager).getFirst();
String actualCitation = CitationStyleGenerator.generateCitation(List.of(testEntry), style, format, context, bibEntryTypesManager).getFirst();
assertEquals(expectedCitation, actualCitation);
}

@Test
void textFormat() {
String expectedCitation = "[1]B. Smith, B. Jones, and J. Williams, “Title of the test entry,” BibTeX Journal, vol. 34, no. 3, pp. 45–67, Jul. 2016, doi: 10.1001/bla.blubb.\n";

BibEntry entry = TestEntry.getTestEntry();
String style = CitationStyle.getDefault().getSource();
CitationStyleOutputFormat format = CitationStyleOutputFormat.TEXT;

String actualCitation = CitationStyleGenerator.generateCitation(List.of(entry), style, format, new BibDatabaseContext(new BibDatabase(List.of(entry))), bibEntryTypesManager).getFirst();
String actualCitation = CitationStyleGenerator.generateCitation(List.of(testEntry), style, format, context, bibEntryTypesManager).getFirst();
assertEquals(expectedCitation, actualCitation);
}

Expand All @@ -134,12 +147,11 @@ void handleDiacritics() {
@Test
void handleAmpersand() {
String expectedCitation = "[1]B. Smith, B. Jones, and J. Williams, “Famous quote: “&TitleTest&” - that is it,” BibTeX Journal, vol. 34, no. 3, pp. 45–67, Jul. 2016, doi: 10.1001/bla.blubb.\n";
BibEntry entry = TestEntry.getTestEntry();
entry.setField(StandardField.TITLE, "Famous quote: “&TitleTest&” - that is it");
testEntry.setField(StandardField.TITLE, "Famous quote: “&TitleTest&” - that is it");
String style = CitationStyle.getDefault().getSource();
CitationStyleOutputFormat format = CitationStyleOutputFormat.TEXT;

String actualCitation = CitationStyleGenerator.generateCitation(List.of(entry), style, format, new BibDatabaseContext(), bibEntryTypesManager).getFirst();
String actualCitation = CitationStyleGenerator.generateCitation(List.of(testEntry), style, format, context, bibEntryTypesManager).getFirst();
assertEquals(expectedCitation, actualCitation);
}

Expand Down Expand Up @@ -208,7 +220,7 @@ static Stream<Arguments> cslMapping() {
.withField(StandardField.ISSUE, "7")
.withField(StandardField.EID, "e0270533"),
"ieee.csl"),
Arguments.of(
Arguments.of(
"[1]F. Last and J. Doe, no. 33, pp. 7–8.\n",
BibDatabaseMode.BIBLATEX,
new BibEntry(StandardEntryType.Article)
Expand Down Expand Up @@ -574,15 +586,14 @@ static Stream<Arguments> cslMapping() {

@ParameterizedTest
@MethodSource
void cslMapping(String expected, BibDatabaseMode mode, BibEntry entry, String cslFileName) throws Exception {
BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(new BibDatabase(List.of(entry)));
bibDatabaseContext.setMode(mode);
void cslMapping(String expected, BibDatabaseMode mode, BibEntry entry, String cslFileName) {
context.setMode(mode);

String citation = CitationStyleGenerator.generateCitation(
List.of(entry),
CitationStyle.createCitationStyleFromFile(cslFileName).orElseThrow().getSource(),
CitationStyleOutputFormat.TEXT,
bibDatabaseContext,
context,
bibEntryTypesManager).getFirst();
assertEquals(expected, citation);
}
Expand Down
Loading