Skip to content

Commit faf0436

Browse files
pluto-hancalixtus
andauthored
Persist citation nature in the CSL reference mark (#15437)
* Persist in-text/empty nature in the CSL reference mark * Backward compatibility with legacy citations * Remove superfluous comments * Add comments in `getUpdatedReferenceMarkNameWithNewNumbers` * Extract citation style and citation type into one method * Reformat code * Update CHANGELOG.md * Transform if-else to switch * Refactor updateMarkAndTextWithNewStyle * use else-if for backward compatibility * update CHANGELOG.md --------- Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com>
1 parent ef82976 commit faf0436

File tree

7 files changed

+165
-112
lines changed

7 files changed

+165
-112
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
2525

2626
### Changed
2727

28+
- We changed CSL reference format by adding citation type at the end. [#15370](https://github.com/JabRef/jabref/issues/15370) [#15434](https://github.com/JabRef/jabref/issues/15434)
2829
- We changed the groups filter field to use a filter icon. [#15402](https://github.com/JabRef/jabref/issues/15402)
2930
- We improved the MultiMergeEntries dialog to automatically select the most plausible field value when merging entries (e.g. a more specific date). [#15027](https://github.com/JabRef/jabref/issues/15027)
3031
- We removed the restart prompt when accepting Mr. DLib privacy settings or hiding the Related articles tab in the entry editor. [#15195](https://github.com/JabRef/jabref/issues/15195)

jablib/src/main/java/org/jabref/logic/openoffice/ReferenceMark.java

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,22 @@
66
import java.util.regex.Matcher;
77
import java.util.regex.Pattern;
88

9+
import org.jabref.logic.openoffice.oocsltext.CSLCitationType;
10+
911
import io.github.thibaultmeyer.cuid.CUID;
1012
import org.slf4j.Logger;
1113
import org.slf4j.LoggerFactory;
1214

1315
public class ReferenceMark {
1416
public static final String[] PREFIXES = {"JABREF_", "CID_"};
1517
public static final String IN_TEXT_MARKER = "IN_TEXT";
18+
public static final String EMPTY_MARKER = "EMPTY";
19+
public static final String NORMAL_MARKER = "NORMAL";
1620

1721
private static final Logger LOGGER = LoggerFactory.getLogger(ReferenceMark.class);
1822

1923
private static final Pattern REFERENCE_MARK_FORMAT = Pattern.compile(
20-
"^(JABREF_[\\w-:—./–]+ CID_\\d+(?:, JABREF_[\\w-:—./–]+ CID_\\d+)*) (\\w+)(?: (" + IN_TEXT_MARKER + "))?$",
24+
"^(JABREF_[\\w-:—./–]+ CID_\\d+(?:, JABREF_[\\w-:—./–]+ CID_\\d+)*) (\\w+)(?: (" + IN_TEXT_MARKER + "|" + EMPTY_MARKER + "|" + NORMAL_MARKER + "))?$",
2125
Pattern.UNICODE_CHARACTER_CLASS);
2226

2327
private static final Pattern ENTRY_PATTERN = Pattern.compile(
@@ -28,7 +32,7 @@ public class ReferenceMark {
2832
private List<String> citationKeys;
2933
private List<Integer> citationNumbers;
3034
private String uniqueId;
31-
private boolean inText;
35+
private CSLCitationType citationType;
3236

3337
/// - Single entry: `JABREF_{citationKey} CID_{citationNumber} {uniqueId}`
3438
/// - Group of entries: `JABREF_{citationKey1} CID_{citationNumber1}, JABREF_{citationKey2} CID_{citationNumber2}, ..., JABREF_{citationKeyN} CID_{citationNumberN} {uniqueId}`
@@ -41,12 +45,12 @@ public ReferenceMark(String name) {
4145
parse(name);
4246
}
4347

44-
public ReferenceMark(String name, List<String> citationKeys, List<Integer> citationNumbers, String uniqueId, boolean inText) {
48+
public ReferenceMark(String name, List<String> citationKeys, List<Integer> citationNumbers, String uniqueId, CSLCitationType citationType) {
4549
this.name = name;
4650
this.citationKeys = citationKeys;
4751
this.citationNumbers = citationNumbers;
4852
this.uniqueId = uniqueId;
49-
this.inText = inText;
53+
this.citationType = citationType;
5054
}
5155

5256
private void parse(String name) {
@@ -56,13 +60,25 @@ private void parse(String name) {
5660
this.citationKeys = List.of(CUID.randomCUID2(8).toString());
5761
this.citationNumbers = List.of(0);
5862
this.uniqueId = this.citationKeys.getFirst();
59-
this.inText = false;
63+
this.citationType = CSLCitationType.NORMAL;
6064
return;
6165
}
6266

6367
String entriesString = matcher.group(1).trim();
6468
this.uniqueId = matcher.group(2) != null ? matcher.group(2).trim() : CUID.randomCUID2(8).toString();
65-
this.inText = matcher.group(3) != null;
69+
70+
String citationTypeMarker = matcher.group(3);
71+
if (citationTypeMarker == null) {
72+
citationTypeMarker = NORMAL_MARKER;
73+
}
74+
this.citationType = switch (citationTypeMarker) {
75+
case IN_TEXT_MARKER ->
76+
CSLCitationType.IN_TEXT;
77+
case EMPTY_MARKER ->
78+
CSLCitationType.EMPTY;
79+
default ->
80+
CSLCitationType.NORMAL;
81+
};
6682

6783
this.citationKeys = new ArrayList<>();
6884
this.citationNumbers = new ArrayList<>();
@@ -77,10 +93,10 @@ private void parse(String name) {
7793
LOGGER.warn("CSLReferenceMark: Failed to parse any entries from name={}. Assuming random values", name);
7894
this.citationKeys = List.of(CUID.randomCUID2(8).toString());
7995
this.citationNumbers = List.of(0);
80-
this.inText = false;
96+
this.citationType = CSLCitationType.NORMAL;
8197
}
8298

83-
LOGGER.debug("CSLReferenceMark: citationKeys={} citationNumbers={} uniqueId={} inText={}", getCitationKeys(), getCitationNumbers(), getUniqueId(), isInText());
99+
LOGGER.debug("CSLReferenceMark: citationKeys={} citationNumbers={} uniqueId={} citationType={}", getCitationKeys(), getCitationNumbers(), getUniqueId(), getCitationType());
84100
}
85101

86102
public String getName() {
@@ -100,8 +116,8 @@ public String getUniqueId() {
100116
return uniqueId;
101117
}
102118

103-
public boolean isInText() {
104-
return inText;
119+
public CSLCitationType getCitationType() {
120+
return citationType;
105121
}
106122

107123
public static Optional<ReferenceMark> of(String name) {

jablib/src/main/java/org/jabref/logic/openoffice/oocsltext/CSLCitationOOAdapter.java

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.sun.star.lang.WrappedTargetException;
2828
import com.sun.star.text.XTextCursor;
2929
import com.sun.star.text.XTextDocument;
30+
import com.sun.star.uno.Exception;
3031

3132
/// This class processes CSL citations in JabRef and interacts directly with LibreOffice using an XTextDocument instance.
3233
/// It is tightly coupled with {@link CSLReferenceMarkManager} for management of reference marks tied to the CSL citations.
@@ -48,9 +49,7 @@ public class CSLCitationOOAdapter {
4849
private final OpenOfficePreferences openOfficePreferences;
4950

5051
private CitationStyle currentStyle;
51-
private boolean styleChanged;
52-
private boolean inTextUsed;
53-
private boolean inTextNatureChanged;
52+
private CSLCitationType citationType;
5453

5554
public CSLCitationOOAdapter(XTextDocument doc, Supplier<List<BibDatabaseContext>> databasesSupplier, OpenOfficePreferences openOfficePreferences, BibEntryTypesManager bibEntryTypesManager) throws WrappedTargetException, NoSuchElementException {
5655
this.document = doc;
@@ -65,36 +64,42 @@ public CSLCitationOOAdapter(XTextDocument doc, Supplier<List<BibDatabaseContext>
6564
}
6665

6766
markManager.readAndUpdateExistingMarks();
68-
this.inTextUsed = markManager.hasInTextMarks();
67+
this.citationType = markManager.getCitationType();
6968
}
7069

71-
public void setStyle(CitationStyle newStyle) {
70+
/// This method is used to determine whether citation style and citation type should be updated
71+
/// Citation type and citation style are extracted into one method for more readability and uniformity
72+
public void setCitationStyleParameters(CitationStyle newStyle, CSLCitationType newCitationType) throws CreationException, Exception {
73+
boolean styleChanged;
74+
boolean citationTypeIsChanged;
75+
7276
if (currentStyle == null || !currentStyle.getName().equals(newStyle.getName())) {
7377
styleChanged = true;
74-
currentStyle = newStyle;
78+
this.currentStyle = newStyle;
7579
} else {
7680
styleChanged = false;
7781
}
82+
83+
if (this.citationType != newCitationType) {
84+
this.citationType = newCitationType;
85+
citationTypeIsChanged = true;
86+
} else {
87+
citationTypeIsChanged = false;
88+
}
89+
90+
if (styleChanged || citationTypeIsChanged) {
91+
updateAllCitationsWithNewStyle(currentStyle, newCitationType);
92+
}
7893
}
7994

8095
/// Inserts a citation for a group of entries.
8196
/// Comparable to LaTeX's \cite command.
8297
public void insertCitation(XTextCursor cursor, CitationStyle selectedStyle, List<BibEntry> entries, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager bibEntryTypesManager)
8398
throws CreationException, com.sun.star.uno.Exception {
84-
setStyle(selectedStyle);
85-
86-
if (inTextUsed) {
87-
inTextNatureChanged = true;
88-
inTextUsed = false;
89-
} else {
90-
inTextNatureChanged = false;
91-
}
92-
99+
// If current citation style is not the same as passed-in citation type, then change it to the new citation style
100+
// If current citation type is not "NORMAL", then change it to "NORMAL".
93101
// Placing this at the beginning reduces the number of updates needed by 1 (in the positive case)
94-
if (styleChanged || inTextNatureChanged) {
95-
updateAllCitationsWithNewStyle(currentStyle, false);
96-
styleChanged = false;
97-
}
102+
setCitationStyleParameters(selectedStyle, CSLCitationType.NORMAL);
98103

99104
String style = selectedStyle.getSource();
100105
boolean isNumericStyle = selectedStyle.isNumericStyle();
@@ -115,7 +120,7 @@ public void insertCitation(XTextCursor cursor, CitationStyle selectedStyle, List
115120
}
116121

117122
OOText ooText = OOFormat.setLocaleNone(OOText.fromString(formattedCitation));
118-
insertReferences(cursor, entries, ooText, isNumericStyle, false);
123+
insertReferences(cursor, entries, ooText, isNumericStyle, CSLCitationType.NORMAL);
119124
}
120125

121126
/// Inserts in-text citations for a group of entries.
@@ -124,19 +129,7 @@ public void insertCitation(XTextCursor cursor, CitationStyle selectedStyle, List
124129
/// @implNote Very similar to the {@link #insertCitation(XTextCursor, CitationStyle, List, BibDatabaseContext, BibEntryTypesManager) insertCitation} method.
125130
public void insertInTextCitation(XTextCursor cursor, CitationStyle selectedStyle, List<BibEntry> entries, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager bibEntryTypesManager)
126131
throws CreationException, com.sun.star.uno.Exception {
127-
setStyle(selectedStyle);
128-
129-
if (!inTextUsed) {
130-
inTextUsed = true;
131-
inTextNatureChanged = true;
132-
} else {
133-
inTextNatureChanged = false;
134-
}
135-
136-
if (styleChanged || inTextNatureChanged) {
137-
updateAllCitationsWithNewStyle(currentStyle, true);
138-
styleChanged = false;
139-
}
132+
setCitationStyleParameters(selectedStyle, CSLCitationType.IN_TEXT);
140133

141134
String style = selectedStyle.getSource();
142135
boolean isNumericStyle = selectedStyle.isNumericStyle();
@@ -172,16 +165,18 @@ public void insertInTextCitation(XTextCursor cursor, CitationStyle selectedStyle
172165
}
173166

174167
OOText ooText = OOFormat.setLocaleNone(OOText.fromString(finalText));
175-
insertReferences(cursor, List.of(currentEntry), ooText, isNumericStyle, true);
168+
insertReferences(cursor, List.of(currentEntry), ooText, isNumericStyle, CSLCitationType.IN_TEXT);
176169
}
177170
}
178171

179172
/// Inserts "empty" citations for a list of entries at the cursor to the document.
180173
/// Adds the entries to the list for which bibliography is to be generated.
181174
public void insertEmptyCitation(XTextCursor cursor, CitationStyle selectedStyle, List<BibEntry> entries)
182175
throws CreationException, com.sun.star.uno.Exception {
176+
setCitationStyleParameters(selectedStyle, CSLCitationType.EMPTY);
177+
183178
OOText emptyOOText = OOFormat.setLocaleNone(OOText.fromString(""));
184-
insertReferences(cursor, entries, emptyOOText, selectedStyle.isNumericStyle(), false);
179+
insertReferences(cursor, entries, emptyOOText, selectedStyle.isNumericStyle(), CSLCitationType.EMPTY);
185180
}
186181

187182
/// Creates a "Bibliography" section in the document and inserts a list of references.
@@ -196,7 +191,7 @@ public void insertBibliography(XTextCursor cursor, CitationStyle selectedStyle,
196191

197192
markManager.setRealTimeNumberUpdateRequired(isNumericStyle);
198193
markManager.readAndUpdateExistingMarks();
199-
updateAllCitationsWithNewStyle(selectedStyle, inTextUsed);
194+
updateAllCitationsWithNewStyle(selectedStyle, citationType);
200195
markManager.readAndUpdateExistingMarks();
201196

202197
OOText title = OOFormat.paragraph(OOText.fromString(openOfficePreferences.getCslBibliographyTitle()), openOfficePreferences.getCslBibliographyHeaderFormat());
@@ -234,7 +229,7 @@ public void insertBibliography(XTextCursor cursor, CitationStyle selectedStyle,
234229
}
235230

236231
/// Inserts references and also adds a space before the citation if not already present ("smart space").
237-
private void insertReferences(XTextCursor cursor, List<BibEntry> entries, OOText ooText, boolean isNumericStyle, boolean isInText)
232+
private void insertReferences(XTextCursor cursor, List<BibEntry> entries, OOText ooText, boolean isNumericStyle, CSLCitationType citationType)
238233
throws CreationException, com.sun.star.uno.Exception {
239234
boolean preceedingSpaceExists;
240235
XTextCursor checkCursor = cursor.getText().createTextCursorByRange(cursor.getStart());
@@ -251,10 +246,10 @@ private void insertReferences(XTextCursor cursor, List<BibEntry> entries, OOText
251246
preceedingSpaceExists = checkCursor.getString().matches("\\R");
252247
}
253248
}
254-
markManager.insertReferenceIntoOO(entries, document, cursor, ooText, !preceedingSpaceExists, openOfficePreferences.getAddSpaceAfter(), isInText);
249+
markManager.insertReferenceIntoOO(entries, document, cursor, ooText, !preceedingSpaceExists, openOfficePreferences.getAddSpaceAfter(), citationType);
255250
markManager.setRealTimeNumberUpdateRequired(isNumericStyle);
256251
markManager.readAndUpdateExistingMarks();
257-
inTextUsed = markManager.hasInTextMarks();
252+
this.citationType = markManager.getCitationType();
258253
}
259254

260255
/// Transforms the numbers in the citation to globally-unique (and thus, reusable) numbers.
@@ -280,7 +275,7 @@ private String updateSingleOrMultipleCitationNumbers(String citation, List<BibEn
280275
/// However, all "generation" of CSL style citations (via {@link CitationStyleGenerator}) occur in this class, and not in {@link CSLReferenceMarkManager}.
281276
/// Furthermore, {@link CSLReferenceMarkManager} is not composed of {@link CitationStyle}.
282277
/// Hence, we keep {@link CSLReferenceMarkManager} independent of {@link CitationStyleGenerator} and {@link CitationStyle}, and keep the following two methods here.
283-
private void updateAllCitationsWithNewStyle(CitationStyle style, boolean isInTextStyle)
278+
private void updateAllCitationsWithNewStyle(CitationStyle style, CSLCitationType citationType)
284279
throws com.sun.star.uno.Exception, CreationException {
285280
boolean isNumericStyle = style.isNumericStyle();
286281
boolean isAlphaNumericStyle = style.isAlphanumericStyle();
@@ -309,7 +304,11 @@ private void updateAllCitationsWithNewStyle(CitationStyle style, boolean isInTex
309304
// Next, we get the list of reference marks sorted in order of appearance in the document
310305
List<CSLReferenceMark> marksInOrder = markManager.getMarksInOrder();
311306

312-
if (isInTextStyle) {
307+
if (citationType == CSLCitationType.EMPTY) {
308+
for (CSLReferenceMark mark : marksInOrder) {
309+
markManager.updateMarkAndTextWithNewStyle(mark, "", CSLCitationType.EMPTY);
310+
}
311+
} else if (citationType == CSLCitationType.IN_TEXT) {
313312
// Now, for each such reference mark, we get the entries to be updated
314313
for (CSLReferenceMark mark : marksInOrder) {
315314
List<String> citationKeys = mark.getCitationKeys();
@@ -350,7 +349,7 @@ private void updateAllCitationsWithNewStyle(CitationStyle style, boolean isInTex
350349
}
351350
}
352351

353-
markManager.updateMarkAndTextWithNewStyle(mark, finalText.toString(), isInTextStyle);
352+
markManager.updateMarkAndTextWithNewStyle(mark, finalText.toString(), CSLCitationType.IN_TEXT);
354353
}
355354
} else {
356355
// Same flow as above - for each such reference mark, we get the entries to be updated
@@ -373,7 +372,7 @@ private void updateAllCitationsWithNewStyle(CitationStyle style, boolean isInTex
373372

374373
String formattedCitation = CSLFormatUtils.transformHTML(newCitation);
375374

376-
markManager.updateMarkAndTextWithNewStyle(mark, formattedCitation, isInTextStyle);
375+
markManager.updateMarkAndTextWithNewStyle(mark, formattedCitation, CSLCitationType.NORMAL);
377376
}
378377
}
379378
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.jabref.logic.openoffice.oocsltext;
2+
3+
public enum CSLCitationType {
4+
NORMAL,
5+
IN_TEXT,
6+
EMPTY
7+
}

jablib/src/main/java/org/jabref/logic/openoffice/oocsltext/CSLReferenceMark.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,26 @@ public class CSLReferenceMark {
1717
private XTextContent textContent;
1818
private final List<String> citationKeys;
1919
private List<Integer> citationNumbers;
20-
private boolean isInText;
20+
private CSLCitationType citationType;
2121

2222
public CSLReferenceMark(XNamed named, ReferenceMark referenceMark) {
2323
this.referenceMark = referenceMark;
2424
this.textContent = UnoRuntime.queryInterface(XTextContent.class, named);
2525
this.citationKeys = referenceMark.getCitationKeys();
2626
this.citationNumbers = referenceMark.getCitationNumbers();
27-
this.isInText = referenceMark.isInText();
27+
this.citationType = referenceMark.getCitationType();
2828
}
2929

30-
public static CSLReferenceMark of(List<String> citationKeys, List<Integer> citationNumbers, boolean inText, XMultiServiceFactory factory) throws Exception {
30+
public static CSLReferenceMark of(List<String> citationKeys, List<Integer> citationNumbers, CSLCitationType citationType, XMultiServiceFactory factory) throws Exception {
3131
String uniqueId = CUID.randomCUID2(8).toString();
32-
String name = buildReferenceName(citationKeys, citationNumbers, uniqueId, inText);
32+
String name = buildReferenceName(citationKeys, citationNumbers, uniqueId, citationType);
3333
XNamed named = UnoRuntime.queryInterface(XNamed.class, factory.createInstance("com.sun.star.text.ReferenceMark"));
3434
named.setName(name);
35-
ReferenceMark referenceMark = new ReferenceMark(name, citationKeys, citationNumbers, uniqueId, inText);
35+
ReferenceMark referenceMark = new ReferenceMark(name, citationKeys, citationNumbers, uniqueId, citationType);
3636
return new CSLReferenceMark(named, referenceMark);
3737
}
3838

39-
private static String buildReferenceName(List<String> citationKeys, List<Integer> citationNumbers, String uniqueId, boolean inText) {
39+
private static String buildReferenceName(List<String> citationKeys, List<Integer> citationNumbers, String uniqueId, CSLCitationType citationType) {
4040
StringBuilder nameBuilder = new StringBuilder();
4141
for (int i = 0; i < citationKeys.size(); i++) {
4242
if (i > 0) {
@@ -46,8 +46,15 @@ private static String buildReferenceName(List<String> citationKeys, List<Integer
4646
.append(" ").append(ReferenceMark.PREFIXES[1]).append(citationNumbers.get(i));
4747
}
4848
nameBuilder.append(" ").append(uniqueId);
49-
if (inText) {
50-
nameBuilder.append(" ").append(ReferenceMark.IN_TEXT_MARKER);
49+
50+
// Embed citation nature into reference mark
51+
switch (citationType) {
52+
case IN_TEXT ->
53+
nameBuilder.append(" ").append(ReferenceMark.IN_TEXT_MARKER);
54+
case EMPTY ->
55+
nameBuilder.append(" ").append(ReferenceMark.EMPTY_MARKER);
56+
case NORMAL ->
57+
nameBuilder.append(" ").append(ReferenceMark.NORMAL_MARKER);
5158
}
5259
return nameBuilder.toString();
5360
}
@@ -73,6 +80,6 @@ public void updateTextContent(XTextContent newTextContent) {
7380
}
7481

7582
public void updateName(String newName) {
76-
this.referenceMark = new ReferenceMark(newName, this.citationKeys, this.citationNumbers, this.referenceMark.getUniqueId(), this.isInText);
83+
this.referenceMark = new ReferenceMark(newName, this.citationKeys, this.citationNumbers, this.referenceMark.getUniqueId(), this.citationType);
7784
}
7885
}

0 commit comments

Comments
 (0)