Skip to content
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

Added support for (biblatex) langid to be an optional field in entry editor #12071

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We added a different background color to the search bar to indicate when the search syntax is wrong. [#11658](https://github.com/JabRef/jabref/pull/11658)
- We added a setting which always adds the literal "Cited on pages" text before each JStyle citation. [#11691](https://github.com/JabRef/jabref/pull/11732)
- We added a new plain citation parser that uses LLMs. [#11825](https://github.com/JabRef/jabref/issues/11825)
- We added support for `langid` field for biblatex libraries. [#10868](https://github.com/JabRef/jabref/issues/10868)
- We added support for modifier keys when dropping a file on an entry in the main table. [#12001](https://github.com/JabRef/jabref/pull/12001)
- We added an importer for SSRN URLs. [#12021](https://github.com/JabRef/jabref/pull/12021)
- We added a compare button to the duplicates in the citation relations tab to open the "Possible duplicate entries" window. [#11192](https://github.com/JabRef/jabref/issues/11192)
Expand Down Expand Up @@ -68,6 +69,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We changed the name of the library-based file directory from 'General File Directory' to 'Library-specific File Directory' per issue [#571](https://github.com/koppor/jabref/issues/571)
- The CitationKey column is now a default shown column for the entry table. [#10510](https://github.com/JabRef/jabref/issues/10510)


### Fixed

- We fixed an issue where certain actions were not disabled when no libraries were open. [#11923](https://github.com/JabRef/jabref/issues/11923)
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ org.gradle.jvmargs=-Xmx6096M

# hint by https://docs.gradle.org/current/userguide/performance.html#enable_the_build_cache
org.gradle.caching=true
org.gradle.parallel=true
Copy link
Member

Choose a reason for hiding this comment

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

Revert this change.

6 changes: 5 additions & 1 deletion src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.jabref.gui.autocompleter.SuggestionProvider;
import org.jabref.gui.autocompleter.SuggestionProviders;
import org.jabref.gui.fieldeditors.identifier.IdentifierEditor;
import org.jabref.gui.fieldeditors.optioneditors.LanguageEditorViewModel;
import org.jabref.gui.fieldeditors.optioneditors.MonthEditorViewModel;
import org.jabref.gui.fieldeditors.optioneditors.OptionEditor;
import org.jabref.gui.fieldeditors.optioneditors.mapbased.CustomFieldEditorViewModel;
Expand Down Expand Up @@ -89,7 +90,10 @@ public static FieldEditorFX getForField(final Field field,
} else if (fieldProperties.contains(FieldProperty.YES_NO)) {
return new OptionEditor<>(new YesNoEditorViewModel(field, suggestionProvider, fieldCheckers, undoManager));
} else if (fieldProperties.contains(FieldProperty.MONTH)) {
return new OptionEditor<>(new MonthEditorViewModel(field, suggestionProvider, databaseContext.getMode(), fieldCheckers, undoManager));
return new OptionEditor<>(new
MonthEditorViewModel(field, suggestionProvider, databaseContext.getMode(), fieldCheckers, undoManager));
} else if (fieldProperties.contains(FieldProperty.LANGUAGE)) {
return new OptionEditor<>(new LanguageEditorViewModel(field, suggestionProvider, databaseContext.getMode(), fieldCheckers, undoManager));
} else if (field == StandardField.GENDER) {
return new OptionEditor<>(new GenderEditorViewModel(field, suggestionProvider, fieldCheckers, undoManager));
} else if (fieldProperties.contains(FieldProperty.EDITOR_TYPE)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.jabref.gui.fieldeditors.optioneditors;

import java.util.Arrays;
import java.util.Collection;

import javax.swing.undo.UndoManager;

import javafx.util.StringConverter;

import org.jabref.gui.autocompleter.SuggestionProvider;
import org.jabref.logic.integrity.FieldCheckers;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.Langid;
import org.jabref.model.entry.field.Field;
import org.jabref.model.strings.StringUtil;

public class LanguageEditorViewModel extends OptionEditorViewModel<Langid> {
private BibDatabaseMode databaseMode;

public LanguageEditorViewModel(Field field, SuggestionProvider<?> suggestionProvider, BibDatabaseMode databaseMode, FieldCheckers fieldCheckers, UndoManager undoManager) {
super(field, suggestionProvider, fieldCheckers, undoManager);
this.databaseMode = databaseMode;
}

@Override
public StringConverter<Langid> getStringConverter() {
return new StringConverter<>() {
@Override
public String toString(Langid object) {
if (object == null) {
return null;
} else {
return object.getLangid(); // Langid used as both display and value
}
}

@Override
public Langid fromString(String string) {
if (StringUtil.isNotBlank(string)) {
return Langid.parse(string).orElse(null);
} else {
return null;
}
}
};
}

@Override
public Collection<Langid> getItems() {
return Arrays.asList(Langid.values());
}

@Override
public String convertToDisplayText(Langid object) {
return object.getName(); // Langid and display text are the same
}
}
4 changes: 4 additions & 0 deletions src/main/java/org/jabref/model/entry/BibEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ public Optional<FieldChange> setMonth(Month parsedMonth) {
return setField(StandardField.MONTH, parsedMonth.getJabRefFormat());
}

public Optional<FieldChange> setLangid(Langid parsedLangid) {
return setField(StandardField.LANGUAGEID, parsedLangid.getJabRefFormat());
}

public Optional<String> getResolvedFieldOrAlias(OrFields fields, BibDatabase database) {
for (Field field : fields.getFields()) {
Optional<String> value = getResolvedFieldOrAlias(field, database);
Expand Down
95 changes: 95 additions & 0 deletions src/main/java/org/jabref/model/entry/Langid.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package org.jabref.model.entry;

import java.util.Optional;

import org.jabref.logic.bibtex.FieldWriter;
/**
* Language identifiers based on BibLaTeX manual specifications.
* See the BibLaTeX documentation for full details:
* <a href="http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/biblatex.pdfhangelo">BibLaTeX manual</a>
*/

Copy link
Member

Choose a reason for hiding this comment

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

JavaDoc needs to be attached to class --> remove empty line here (and add empty line above)

public enum Langid {
mag-sun marked this conversation as resolved.
Show resolved Hide resolved
BASQUE("Basque", "basque"),
BULGARIAN("Bulgarian", "bulgarian"),
CATALAN("Catalan", "catalan"),
CROATIAN("Croatian", "croatian"),
CZECH("Czech", "czech"),
DANISH("Danish", "danish"),
AMERICAN("American", "american"),
USENGLISH("US English", "USenglish"),
ENGLISH("English", "english"),
BRITISH("British", "british"),
UKENGLISH("UK English", "UKenglish"),
CANADIAN("Canadian", "canadian"),
AUSTRALIAN("Australian", "australian"),
NEWZEALAND("New Zealand", "newzealand"),
ESTONIAN("Estonian", "estonian"),
FINNISH("Finnish", "finnish"),
FRENCH("French", "french"),
GERMAN("German", "german"),
AUSTRIAN("Austrian", "austrian"),
SWISSGERMAN("Swiss German", "swissgerman"),
NGERMAN("German (New)", "ngerman"),
NAUSTRIAN("Austrian (New)", "naustrian"),
NSWISSGERMAN("Swiss German (New)", "nswissgerman"),
GREEK("Greek", "greek"),
MAGYAR("Hungarian", "hungarian"),
HUNGARIAN("Hungarian", "hungarian"),
ICELANDIC("Icelandic", "icelandic"),
ITALIAN("Italian", "italian"),
LATVIAN("Latvian", "latvian"),
LITHUANIAN("Lithuanian", "lithuanian"),
MARATHI("Marathi", "marathi"),
NORSK("Norwegian (Bokmål)", "norsk"),
NYNORSK("Norwegian (Nynorsk)", "nynorsk"),
POLISH("Polish", "polish"),
BRAZIL("Portuguese (Brazilian)", "brazil"),
PORTUGUESE("Portuguese", "portuguese"),
PORTUGES("Portuguese (alt)", "portuges"),
ROMANIAN("Romanian", "romanian"),
RUSSIAN("Russian", "russian"),
SERBIAN("Serbian (Latin)", "serbian"),
SERBIANC("Serbian (Cyrillic)", "serbianc"),
SLOVAK("Slovak", "slovak"),
SLOVENE("Slovene", "slovene"),
SLOVENIAN("Slovenian", "slovenian"),
SPANISH("Spanish", "spanish"),
SWEDISH("Swedish", "swedish"),
TURKISH("Turkish", "turkish"),
UKRAINIAN("Ukrainian", "ukrainian");


private final String name;
private final String langid;

Langid(String name, String langid) {
this.name = name;
this.langid = langid;
}

public String getLangid() {
return langid;
}

public String getName() {
return name;
}

public static Optional<Langid> getByLangid(String id) {
for (Langid lang : Langid.values()) {
if (lang.langid.equalsIgnoreCase(id)) {
return Optional.of(lang);
}
}
return Optional.empty();
}

public static Optional<Langid> parse(String value) {
return Langid.getByLangid(value.trim().toLowerCase());
}

public String getJabRefFormat() {
return (FieldWriter.BIBTEX_STRING_START_END_SYMBOL + "%s" + FieldWriter.BIBTEX_STRING_START_END_SYMBOL).formatted(langid);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public static Collection<Field> getNotTextFields() {
// These fields are not marked as verbatim, because they could include LaTeX code
result.add(StandardField.MONTH);
result.add(StandardField.DATE);
result.add(StandardField.LANGUAGEID);
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class BiblatexEntryTypeDefinitions {
.withImportantFields(
StandardField.SUBTITLE, StandardField.EDITOR, StandardField.SERIES, StandardField.VOLUME, StandardField.NUMBER,
StandardField.EID, StandardField.ISSUE, StandardField.PAGES, StandardField.NOTE, StandardField.ISSN, StandardField.DOI,
StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE)
StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE, StandardField.LANGUAGEID)
.withDetailFields(
StandardField.TRANSLATOR, StandardField.ANNOTATOR, StandardField.COMMENTATOR,
StandardField.TITLEADDON, StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC,
Expand All @@ -39,7 +39,7 @@ public class BiblatexEntryTypeDefinitions {
StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.MAINTITLE, StandardField.MAINSUBTITLE,
StandardField.MAINTITLEADDON, StandardField.VOLUME, StandardField.EDITION, StandardField.PUBLISHER, StandardField.ISBN,
StandardField.CHAPTER, StandardField.PAGES, StandardField.PAGETOTAL, StandardField.DOI, StandardField.EPRINT,
StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE)
StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE, StandardField.LANGUAGEID)
.withDetailFields(StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC,
StandardField.TRANSLATOR, StandardField.ANNOTATOR, StandardField.COMMENTATOR, StandardField.INTRODUCTION,
StandardField.FOREWORD, StandardField.AFTERWORD,
Expand All @@ -55,7 +55,7 @@ public class BiblatexEntryTypeDefinitions {
.withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.DATE)
.withImportantFields(StandardField.EDITOR, StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.EDITION,
StandardField.PUBLISHER, StandardField.ISBN, StandardField.PAGETOTAL, StandardField.DOI, StandardField.EPRINT,
StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE)
StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE, StandardField.LANGUAGEID)
.withDetailFields(StandardField.EDITORA, StandardField.EDITORB, StandardField.EDITORC,
StandardField.TRANSLATOR, StandardField.ANNOTATOR, StandardField.COMMENTATOR, StandardField.INTRODUCTION,
StandardField.FOREWORD, StandardField.AFTERWORD,
Expand All @@ -72,7 +72,7 @@ public class BiblatexEntryTypeDefinitions {
StandardField.MAINTITLE, StandardField.MAINSUBTITLE, StandardField.MAINTITLEADDON, StandardField.BOOKSUBTITLE,
StandardField.BOOKTITLEADDON, StandardField.VOLUME, StandardField.EDITION, StandardField.PUBLISHER,
StandardField.ISBN, StandardField.CHAPTER, StandardField.PAGES, StandardField.DOI, StandardField.EPRINT,
StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE)
StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE, StandardField.LANGUAGEID)
.withDetailFields(StandardField.EDITORA, StandardField.EDITORB,
StandardField.EDITORC, StandardField.TRANSLATOR, StandardField.ANNOTATOR, StandardField.COMMENTATOR,
StandardField.INTRODUCTION, StandardField.FOREWORD, StandardField.AFTERWORD,
Expand Down Expand Up @@ -101,7 +101,7 @@ public class BiblatexEntryTypeDefinitions {
.withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE)
.withImportantFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.HOWPUBLISHED,
StandardField.CHAPTER, StandardField.PAGES, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS,
StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE)
StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE, StandardField.LANGUAGEID)
.withDetailFields(StandardField.LANGUAGE,
StandardField.TYPE, StandardField.NOTE, StandardField.LOCATION,
StandardField.PAGETOTAL, StandardField.ADDENDUM, StandardField.PUBSTATE)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package org.jabref.gui.fieldeditors.optioneditors;

import java.util.Collection;

import javax.swing.undo.UndoManager;

import org.jabref.gui.autocompleter.SuggestionProvider;
import org.jabref.logic.FilePreferences;
import org.jabref.logic.integrity.FieldCheckers;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.Langid;
import org.jabref.model.entry.field.StandardField;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class LanguageEditorViewModelTest {

private LanguageEditorViewModel languageEditorViewModel;

@BeforeEach
void setUp() {
// Mock dependencies
Copy link
Member

Choose a reason for hiding this comment

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

Remove comment. The next lines state that by code.

BibDatabaseContext databaseContext = Mockito.mock(BibDatabaseContext.class);
FilePreferences filePreferences = Mockito.mock(FilePreferences.class);
JournalAbbreviationRepository abbreviationRepository = Mockito.mock(JournalAbbreviationRepository.class);

// Initialize FieldCheckers with mocked instances
Copy link
Member

Choose a reason for hiding this comment

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

Remove comment. The next lines state that by code.

FieldCheckers fieldCheckers = new FieldCheckers(databaseContext, filePreferences, abbreviationRepository, false);

// Mock the SuggestionProvider
Copy link
Member

Choose a reason for hiding this comment

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

Remove comment. The next lines state that by code.

SuggestionProvider<?> suggestionProvider = Mockito.mock(SuggestionProvider.class);

// Initialize the LangidEditorViewModel
Copy link
Member

Choose a reason for hiding this comment

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

Remove comment. The next lines state that by code.

Comments are useful if they add information. E.g. for the reasons of the code... - But not necessary here IMHO.

languageEditorViewModel = new LanguageEditorViewModel(
StandardField.LANGUAGEID, // Use the correct field
Copy link
Member

Choose a reason for hiding this comment

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

Remove comment.

suggestionProvider, // Mocked SuggestionProvider
Copy link
Member

Choose a reason for hiding this comment

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

Remove comment.

BibDatabaseMode.BIBLATEX, // Use the correct BibDatabaseMode
Copy link
Member

Choose a reason for hiding this comment

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

Remove comment.

fieldCheckers, // FieldCheckers instance
Copy link
Member

Choose a reason for hiding this comment

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

Remove comment.

new UndoManager() // UndoManager instance
Copy link
Member

Choose a reason for hiding this comment

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

Remove comment.

);
}

@Test
void getItemsShouldReturnAllLangidValues() {
Collection<Langid> items = languageEditorViewModel.getItems();
assertEquals(Langid.values().length, items.size());
assertTrue(items.contains(Langid.BASQUE)); // Check if it contains a specific Langid
Copy link
Member

Choose a reason for hiding this comment

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

Remove comment.

assertTrue(items.contains(Langid.AMERICAN)); // Additional check for another Langid
Copy link
Member

Choose a reason for hiding this comment

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

Try to rewrite multiple assert statements to one

assertEquals(List.of(...), items)

}

@Test
void testStringConversion() {
String langidString = "bulgarian";
Langid langid = languageEditorViewModel.getStringConverter().fromString(langidString);
assertEquals(Langid.BULGARIAN, langid, "String should convert to the corresponding Langid");

String convertedString = languageEditorViewModel.getStringConverter().toString(Langid.BULGARIAN);
assertEquals(langidString, convertedString, "Langid should convert back to its string representation");
}

@Test
void testStringConversionWithHumanReadableName() {
// Test conversion from human-readable name to Langid
String langidString = "Basque";
Langid langid = languageEditorViewModel.getStringConverter().fromString(langidString);
assertEquals(Langid.BASQUE, langid, "Human-readable name should convert to the corresponding Langid");

// Test conversion from Langid to human-readable name
String convertedString = languageEditorViewModel.getStringConverter().toString(Langid.BASQUE);
assertEquals("basque", convertedString, "Langid should convert back to its lowercase string representation");
}

@Test
void testHandlingNullValue() {
// Test the handling of a null value
Langid result = languageEditorViewModel.getStringConverter().fromString(null);
assertEquals(null, result, "Null input should return null Langid");
}

@Test
void testHandlingBlankValue() {
// Test the handling of a blank string
Langid result = languageEditorViewModel.getStringConverter().fromString(" ");
assertEquals(null, result, "Blank input should return null Langid");
}
}

Loading
Loading