-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
CSL4LibreOffice - D [GSoC '24] #11636
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
Changes from 21 commits
01fdc3e
833be28
1bf6b59
3c0bc52
262287a
3f87126
70a8e47
be680d3
b9b915f
44773dc
c143782
168ac64
9848d78
cc24c98
8d81fbb
20e7947
dd56e89
5840564
5c53a15
95041f2
f81daf0
8a23b93
cd2539c
02fbe4e
4a90528
84759e0
4b25932
cf51468
0255611
0c7e84a
71ab2cd
e132bdf
30b8103
3962402
ff3cb2a
f77c35e
08a712b
2a611de
38a58d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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}). | ||
|
||
| * <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; | ||
|
Member
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 think, this should be separated by a newline from the constant before. Reason: This one should not be configurable, should it?
Member
Author
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. 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 | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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\">" | ||||||
|
|
@@ -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\">" | ||||||
|
|
@@ -58,6 +57,22 @@ void aPACitation() { | |||||
| assertEquals(expected, citation); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
|
Member
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. just mark the test @disabled
Member
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. The comment is IMHO helpful. For someone diving into it 1 year later.
Member
Author
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. Marked disabled and kept the comment. |
||||||
| * The below test, as of now, fails due to citeproc-java returning an empty citation. | ||||||
|
||||||
| * The below test, as of now, fails due to citeproc-java returning an empty citation. | |
| * Fails due to citeproc-java returning an empty citation. |
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.
Done
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.
Why
public? It is used only in this class?If you will need it for testing, make it package private and annotate it with
@VisibleForTestingThere 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.
Done