Skip to content

Enhance customization of entry types#15290

Merged
Siedlerchr merged 50 commits into
JabRef:mainfrom
pluto-han:fix-for-issue-9840
May 11, 2026
Merged

Enhance customization of entry types#15290
Siedlerchr merged 50 commits into
JabRef:mainfrom
pluto-han:fix-for-issue-9840

Conversation

@pluto-han

@pluto-han pluto-han commented Mar 7, 2026

Copy link
Copy Markdown
Collaborator

Related issues and pull requests

Closes #9840

PR Description

In this pr, I added a new data format jabref-entrytype-v2, which use | to separate field names with field properties.
In preference -> entry types, I added property selection checkbox for custom fields in custom entry types, and persisted them using the jabref-entrytype-v2 while keeping jabref-entrytype for compatibility.

Steps to test

  1. Open preference -> entry types

  2. Select Author field, PERSON_NAMES property shows up in field property selection checkbox, but cannot be modified.

image
  1. Type Author1234 field, property can be modified as it is now a custom field. Check BOOK_NAME property and press "add" and save.
image
  1. Add a new entry type Mytype. Add required field test with property BOOK_NAME, optional field "person' with property PERSON_NAMES and optional field title. Save bib file and restart jabref.

  2. bib file should contain:

@Comment{jabref-entrytype-v2: mytype: req[test|BOOK_NAME] opt[person|PERSON_NAMES;title]}

@Comment{jabref-entrytype: mytype: req[test] opt[person;title]}
image

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • I manually tested my changes in running JabRef (always required)
  • I added JUnit tests for changes (if applicable)
  • I added screenshots in the PR description (if change is visible to the user)
  • I added a screenshot in the PR description showing a library with a single entry with me as author and as title the issue number
  • I described the change in CHANGELOG.md in a way that can be understood by the average user (if change is visible to the user)
  • I checked the user documentation for up to dateness and submitted a pull request to our user documentation repository

Required verification screenshot

7d6fba8139d263b44936cb08576891f1

@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add field property selection for custom entry types with v2 format support

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add field property selection UI for custom entry types
• Implement new jabref-entrytype-v2 format with field properties
• Support parsing and serializing field properties using | separator
• Maintain backward compatibility with v1 format in preferences
Diagram
flowchart LR
  UI["UI: CheckComboBox<br/>Field Properties"]
  FieldVM["FieldViewModel<br/>Store Properties"]
  Serializer["MetaDataSerializer<br/>v2 Format"]
  Parser["MetaDataParser<br/>Parse v2"]
  Storage["Preferences<br/>v1 + v2"]
  
  UI -->|"Selected Properties"| FieldVM
  FieldVM -->|"Serialize"| Serializer
  Serializer -->|"Write to .bib"| Storage
  Storage -->|"Read from .bib"| Parser
  Parser -->|"Restore Properties"| FieldVM
Loading

Grey Divider

File Changes

1. jabgui/src/main/java/org/jabref/gui/preferences/customentrytypes/CustomEntryTypesTab.java ✨ Enhancement +76/-1

Add field property checkbox UI and selection logic

jabgui/src/main/java/org/jabref/gui/preferences/customentrytypes/CustomEntryTypesTab.java


2. jabgui/src/main/java/org/jabref/gui/preferences/customentrytypes/CustomEntryTypesTabViewModel.java ✨ Enhancement +38/-7

Handle field properties when adding custom fields

jabgui/src/main/java/org/jabref/gui/preferences/customentrytypes/CustomEntryTypesTabViewModel.java


3. jabgui/src/main/java/org/jabref/gui/preferences/customentrytypes/FieldViewModel.java ✨ Enhancement +15/-0

Store and manage field properties in view model

jabgui/src/main/java/org/jabref/gui/preferences/customentrytypes/FieldViewModel.java


View more (13)
4. jabgui/src/main/resources/org/jabref/gui/preferences/customentrytypes/CustomEntryTypesTab.fxml ✨ Enhancement +2/-0

Add CheckComboBox for field property selection

jabgui/src/main/resources/org/jabref/gui/preferences/customentrytypes/CustomEntryTypesTab.fxml


5. jablib/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java ✨ Enhancement +5/-0

Write both v1 and v2 entry type formats

jablib/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java


6. jablib/src/main/java/org/jabref/logic/exporter/MetaDataSerializer.java ✨ Enhancement +12/-0

Add v2 serialization method with field properties

jablib/src/main/java/org/jabref/logic/exporter/MetaDataSerializer.java


7. jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java ✨ Enhancement +1/-1

Support parsing v2 entry type format

jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java


8. jablib/src/main/java/org/jabref/logic/importer/util/MetaDataParser.java ✨ Enhancement +3/-0

Parse v2 format with field properties

jablib/src/main/java/org/jabref/logic/importer/util/MetaDataParser.java


9. jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java ✨ Enhancement +63/-0

Parse and serialize fields with properties using separator

jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java


10. jablib/src/main/java/org/jabref/model/metadata/MetaData.java ✨ Enhancement +1/-0

Add v2 entry type flag constant

jablib/src/main/java/org/jabref/model/metadata/MetaData.java


11. jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java ✨ Enhancement +39/-11

Store v2 entry types separately with backward compatibility

jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java


12. jablib/src/test/java/org/jabref/logic/exporter/BibDatabaseWriterTest.java 🧪 Tests +33/-0

Add tests for v2 format with field properties

jablib/src/test/java/org/jabref/logic/exporter/BibDatabaseWriterTest.java


13. jablib/src/test/java/org/jabref/logic/exporter/MetaDataSerializerTest.java 🧪 Tests +54/-0

Test v2 serialization with field properties

jablib/src/test/java/org/jabref/logic/exporter/MetaDataSerializerTest.java


14. jablib/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java 🧪 Tests +46/-0

Test v2 parsing and property preservation

jablib/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java


15. jablib/src/test/java/org/jabref/logic/importer/util/MetaDataParserTest.java 🧪 Tests +18/-0

Test v2 format parsing with properties

jablib/src/test/java/org/jabref/logic/importer/util/MetaDataParserTest.java


16. CHANGELOG.md 📝 Documentation +1/-0

Document new field property feature

CHANGELOG.md


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. findExistingField() uses Optional.get() ✓ Resolved 📘 Rule violation ≡ Correctness
Description
findExistingField calls .findFirst().get() which can throw NoSuchElementException if the field
is not found. This risks runtime crashes and violates the requirement to avoid unsafe Optional
access.
Code

jabgui/src/main/java/org/jabref/gui/preferences/customentrytypes/CustomEntryTypesTabViewModel.java[R199-208]

+    public FieldViewModel findExistingField(Field field) {
+        String displayName = FieldTextMapper.getDisplayName(field);
+        return selectedEntryType.getValue()
+                                .fields()
+                                .stream()
+                                .filter(fieldViewModel ->
+                                        fieldViewModel.displayNameProperty().getValue()
+                                                      .equalsIgnoreCase(displayName))
+                                .findFirst().get();
+    }
Evidence
Compliance ID 28 requires avoiding unsafe Optional access patterns; the new code uses
Optional.get() directly on the result of findFirst() without safe handling.

jabgui/src/main/java/org/jabref/gui/preferences/customentrytypes/CustomEntryTypesTabViewModel.java[199-208]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`findExistingField` uses `.findFirst().get()` which can throw at runtime if no matching field exists.
## Issue Context
The compliance rules require safe Optional access patterns to prevent crashes.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/preferences/customentrytypes/CustomEntryTypesTabViewModel.java[199-208]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Blank properties can crash parse ✓ Resolved 📘 Rule violation ≡ Correctness
Description
FieldFactory.parseField can throw IllegalArgumentException when parsing v2 field strings like
field| (blank property token). This fails to guard against empty/missing values and can crash when
reading malformed comments/preferences.
Code

jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java[R63-79]

+        if (fieldName.contains(FIELD_NAME_PROPERTY_SEPARATOR)) {
+            String[] fieldAndProperties = fieldName.split(Pattern.quote(FIELD_NAME_PROPERTY_SEPARATOR));
+
+            if (fieldAndProperties.length == 2) {
+                String unknownFieldName = fieldAndProperties[0];
+                String[] fieldProperties = fieldAndProperties[1].split(FIELD_PROPERTY_SEPARATOR);
+
+                if (fieldProperties.length == 0) {
+                    return new UnknownField(unknownFieldName);
+                } else {
+                    FieldProperty firstProperty = FieldProperty.valueOf(fieldProperties[0]);
+                    FieldProperty[] restProperties = Arrays.stream(fieldProperties, 1, fieldProperties.length)
+                                                           .map(FieldProperty::valueOf)
+                                                           .toArray(FieldProperty[]::new);
+
+                    return new UnknownField(unknownFieldName, firstProperty, restProperties);
+                }
Evidence
Compliance ID 28 requires guarding against missing/blank values before use; the new v2 parsing path
calls FieldProperty.valueOf(fieldProperties[0]) without checking for blank/invalid tokens, which
can throw.

jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java[63-79]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The v2 parsing logic in `FieldFactory.parseField` can throw if the property part is empty/blank (e.g., `name|`) or contains invalid enum names.
## Issue Context
This code parses data from comments/preferences, which can be malformed. Compliance requires guarding against missing/blank values.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java[63-79]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Typo serializezOrFieldsV2 ✓ Resolved 📘 Rule violation ≡ Correctness
Description
The new method name serializezOrFieldsV2 contains a spelling typo, reducing readability and
discoverability. This violates the requirement for intent-revealing, correctly spelled identifiers.
Code

jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java[R156-167]

+    public static String serializezOrFieldsV2(OrFields fields) {
+        return fields.getFields().stream()
+                     .map(field -> {
+                         if (field instanceof UnknownField unknownField) {
+                             return serializeUnknownField(unknownField);
+                         } else {
+                             return field.getName();
+                         }
+                     })
+                     .collect(Collectors.joining(FIELD_OR_SEPARATOR));
+    }
+
Evidence
Compliance ID 8 requires correctly spelled, intent-revealing names; serializezOrFieldsV2
introduces a typo in a public method name.

AGENTS.md
jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java[156-167]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new API method name contains a typo: `serializezOrFieldsV2`.
## Issue Context
Naming consistency and correct spelling are required for maintainability.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java[156-167]
- jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java[185-187]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. V2 properties dropped ✓ Resolved 🐞 Bug ≡ Correctness
Description
When a .bib contains both v1 and v2 entrytype comments but v1 appears first, BibTeX parsing can
ignore the later v2 definition (losing properties) because entry types are stored in a HashSet and
equality ignores field properties (and even field priority).
Code

jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[R386-389]

+        } else if (comment.startsWith(MetaData.ENTRYTYPE_FLAG_V2) || comment.startsWith(MetaData.ENTRYTYPE_FLAG)) {
// A custom entry type can also be stored in a
// "@comment"
Optional<BibEntryType> typ = MetaDataParser.parseCustomEntryType(comment);
Evidence
BibtexParser collects parsed entry types in a HashSet and blindly calls entryTypes.add(typ).
Since BibField.equals compares only field names (ignoring priority) and UnknownField.equals
compares only the name (ignoring properties), a v1 and v2 definition for the same entry type/fields
will compare equal, so whichever one is seen first wins. That makes v2-property loading dependent on
comment order in the file.

jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[208-213]
jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[386-392]
jablib/src/main/java/org/jabref/model/entry/field/UnknownField.java[46-60]
jablib/src/main/java/org/jabref/model/entry/field/BibField.java[5-21]
jablib/src/main/java/org/jabref/model/entry/BibEntryType.java[123-140]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
BibTeX import stores custom entry type definitions in a `HashSet&amp;amp;lt;BibEntryType&amp;amp;gt;`. Because `BibField` and `UnknownField` equality ignore properties, a v1 and v2 definition of the same type/fields compare equal; if v1 is encountered first, the later v2 definition is ignored and properties are lost.
## Issue Context
This affects parsing of .bib files containing both:
- `@Comment{jabref-entrytype: ...}` (v1)
- `@Comment{jabref-entrytype-v2: ...}` (v2)
When the v1 comment appears first (e.g., manual edits, merges, external tools reordering comments), v2 properties will not be applied.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[386-398]
- jablib/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java[208-213]
## Suggested approach
1. In `BibtexParser.parseJabRefComment`, after successfully parsing an entry type:
- If the comment starts with `MetaData.ENTRYTYPE_FLAG_V2`, remove any existing entry type for the same `EntryType` name (case-insensitive) from `entryTypes` and then add the v2 one.
- Keep current behavior for v1 (just `add`), so v1 never overwrites v2.
2. Implement removal by matching on `typ.get().getType().getName()` rather than relying on `equals`, to handle cases where v1/v2 field lists differ.
3. Add/extend a test where the file contains both comments but with v1 first, and assert that properties from v2 are present in the resulting `ParserResult.getEntryTypes()`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java
@testlens-app

This comment has been minimized.

@github-actions github-actions Bot added the status: changes-required Pull requests that are not yet complete label Mar 7, 2026
@testlens-app

This comment has been minimized.

@testlens-app

This comment has been minimized.

@testlens-app

This comment has been minimized.

@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels Mar 7, 2026
@testlens-app

This comment has been minimized.

@pluto-han

pluto-han commented Apr 10, 2026

Copy link
Copy Markdown
Collaborator Author

If the problem above is solved, this idea would be great. Now adding field logic will be:

  1. Users add a new field, click "add". A new field is added.
  2. Users select property for the field, click "cancel" to return to the latest save. Click "Save" to update property.
  3. Users click "revert" to clear the properties of the field.

I'm still thinking about it. Mybe we can use this:

  1. users add a new field, and press "add':
image
  1. users modify properties of the field, press "save", properties in checked boxes are saved.

  2. users modify properties again, but are not happy about this modification, so they press "revert" to go back to the latest save.

image

@github-actions github-actions Bot added status: changes-required Pull requests that are not yet complete and removed status: no-bot-comments labels Apr 14, 2026
@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels May 1, 2026
@Siedlerchr Siedlerchr added the status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers label May 5, 2026
@Siedlerchr

Copy link
Copy Markdown
Member

Can you take a look at the failing Windows test? And also take a look at the QODO point regarding the parsing order?

* upstream/main: (21 commits)
  chore(deps): update dependency com.konghq:unirest-modules-gson to v4.10.0 (JabRef#15715)
  Add manual tests (JabRef#15351)
  Refactored the comments for UnlinkedFilesCrawler (JabRef#15709)
  Replace inline styles with CSS classes (JabRef#15694)
  add test case for multiple authors in csl citaiton (JabRef#15707)
  fix invalid desktop file for linux (JabRef#15702)
  Change FileKeystore and Folder fields to disable/enable (JabRef#15685)
  Chore(deps): Bump org.openrewrite.recipe:rewrite-recipe-bom from 3.30.0 to 3.30.1 (JabRef#15696)
  New Crowdin updates (JabRef#15693)
  Chore(deps): Bump dev.langchain4j:langchain4j-bom in /versions (JabRef#15698)
  Chore(deps): Bump org.apache.logging.log4j:log4j-to-slf4j in /versions (JabRef#15700)
  chore(deps): update dependency org.apache.logging.log4j:log4j-to-slf4j to v2.26.0 (JabRef#15699)
  Chore(deps): Bump org.openrewrite.rewrite from 7.32.1 to 7.32.2 (JabRef#15697)
  Chore(deps): Bump jablib/src/main/resources/csl-locales (JabRef#15689)
  Fix month checker regex (JabRef#15678)
  Chore(deps): Bump com.dlsc.gemsfx:gemsfx in /versions (JabRef#15692)
  Chore(deps): Bump org.hisp.dhis:json-tree in /versions (JabRef#15691)
  Chore(deps): Bump jablib/src/main/resources/csl-styles (JabRef#15690)
  New Crowdin updates (JabRef#15687)
  Chore(deps): Bump com.konghq:unirest-java-core in /versions (JabRef#15683)
  ...
@Siedlerchr Siedlerchr enabled auto-merge May 11, 2026 17:59
Siedlerchr
Siedlerchr previously approved these changes May 11, 2026
@pluto-han

pluto-han commented May 11, 2026

Copy link
Copy Markdown
Collaborator Author

Can you take a look at the failing Windows test? And also take a look at the QODO point regarding the parsing order?

I'm sorry for the late response, just saw this comment. GitHub did not notify me:(

Edit: The Windows test fails again, I'll work on it.

@Siedlerchr

Copy link
Copy Markdown
Member

My bet is something with the OS.newline stuff somehwere in writing or you have a harcoded \n somewhere


databaseWriter.writeDatabase(bibtexContext);

assertEquals("""

@Siedlerchr Siedlerchr May 11, 2026

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 textblocks use internally \n that could explain the windows failure

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes the internally \n is the culprit

@Siedlerchr Siedlerchr added this pull request to the merge queue May 11, 2026
@github-actions github-actions Bot added the status: to-be-merged PRs which are accepted and should go into the merge-queue. label May 11, 2026
Merged via the queue into JabRef:main with commit 3760716 May 11, 2026
54 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

good third issue status: no-bot-comments status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers status: to-be-merged PRs which are accepted and should go into the merge-queue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhance customization of entry types

4 participants