Skip to content

Conversation

@keerthansamudrala
Copy link

@keerthansamudrala keerthansamudrala commented Dec 28, 2025

User description

Closes #14457

I have implemented the MonthPlausibilityComparator to handle month field reconciliation. This comparator uses a priority-based hierarchy (JabRef format > BibTeX short name > Numbers) to suggest the most standard month format when merging or comparing entries.

Note to Reviewers: I have included temporary comments within the logic to explain the specific decision-making for each "Tier" (JabRef format, Short names, Numbers, etc.). If the logic and implementation meet the project standards, I will remove these comments to provide a final "clean code" version for merging.

Steps to test

1. Run the newly added JUnit test :
org.jabref.logic.bibtex.comparator.plausibility.MonthPlausibilityComparatorTest.

2. Verify that the 16 tests pass, covering scenarios such as:
- Preference for #jan# over jan.
- Preference for jan over January.
- Preference for 1 over January.
- Correct handling of conflicting months (e.g., June vs. July) by returning UNDETERMINED.
Screenshot 2025-12-28 at 3 34 00 PM

Mandatory checks


PR Type

Enhancement, Tests


Description

  • Implemented MonthPlausibilityComparator for month field reconciliation

  • Uses priority-based hierarchy: JabRef format > short names > numbers

  • Added 16 comprehensive JUnit tests covering various month format scenarios

  • Integrated comparator into PlausibilityComparatorFactory for automatic field handling


Diagram Walkthrough

flowchart LR
  A["Month Field Values"] -- "parse" --> B["MonthPlausibilityComparator"]
  B -- "priority hierarchy" --> C["JabRef Format"]
  B -- "priority hierarchy" --> D["Short Names"]
  B -- "priority hierarchy" --> E["Numbers"]
  B -- "priority hierarchy" --> F["String Length"]
  C --> G["ComparisonResult"]
  D --> G
  E --> G
  F --> G
  H["PlausibilityComparatorFactory"] -- "register" --> B
Loading

File Walkthrough

Relevant files
Enhancement
MonthPlausibilityComparator.java
New MonthPlausibilityComparator implementation                     

jablib/src/main/java/org/jabref/logic/bibtex/comparator/plausibility/MonthPlausibilityComparator.java

  • New comparator implementing FieldValuePlausibilityComparator interface
  • Implements priority-based month format comparison: JabRef format
    (#jan#) > short names (jan) > numbers (1) > string length
  • Handles invalid month values and conflicting months by returning
    UNDETERMINED
  • Uses Month.parse() to validate and normalize month values
+79/-0   
PlausibilityComparatorFactory.java
Register MonthPlausibilityComparator in factory                   

jablib/src/main/java/org/jabref/logic/bibtex/comparator/plausibility/PlausibilityComparatorFactory.java

  • Added registration of MonthPlausibilityComparator for fields with
    FieldProperty.MONTH
  • Integrated new comparator into factory's getPlausibilityComparator
    method
  • Follows existing pattern for other field type comparators
+3/-0     
Tests
MonthPlausibilityComparatorTest.java
Comprehensive test suite for MonthPlausibilityComparator 

jablib/src/test/java/org/jabref/logic/bibtex/comparator/plausibility/MonthPlausibilityComparatorTest.java

  • New parameterized JUnit test class with 16 test cases
  • Tests preference hierarchy: JabRef format over short names, short
    names over numbers, numbers over full names
  • Tests edge cases: invalid months, empty strings, conflicting months,
    case sensitivity
  • Validates correct handling of UNDETERMINED results for conflicting or
    invalid inputs
+18/-0   
Formatting
EntryTypePlausibilityComparator.java
Reformat JavaDoc comment formatting                                           

jablib/src/main/java/org/jabref/logic/bibtex/comparator/plausibility/EntryTypePlausibilityComparator.java

  • Reformatted JavaDoc comment from single line to multi-line format
  • No functional changes to the comparator logic
+13/-1   
Configuration changes
Project.xml
Update IDE code style configuration                                           

.idea/codeStyles/Project.xml

  • Removed ENABLE_JAVADOC_FORMATTING option setting
  • Added WRAP_COMMENTS option set to true for JAVA code style
  • IDE code style configuration updates
+1/-1     

@github-actions
Copy link
Contributor

Hey @keerthansamudrala! 👋

Thank you for contributing to JabRef!

We have automated checks in place, based on which you will soon get feedback if any of them are failing. We also use Qodo for review assistance. It will update your pull request description with a review help and offer suggestions to improve the pull request.

After all automated checks pass, a maintainer will also review your contribution. Once that happens, you can go through their comments in the "Files changed" tab and act on them, or reply to the conversation if you have further inputs. You can read about the whole pull request process in our contribution guide.

Please ensure that your pull request is in line with our AI Usage Policy and make necessary disclosures.

@github-actions github-actions bot added first contrib good first issue An issue intended for project-newcomers. Varies in difficulty. labels Dec 28, 2025
@qodo-free-for-open-source-projects
Copy link
Contributor

qodo-free-for-open-source-projects bot commented Dec 28, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing Integer Parsing Validation: Line 60 uses Integer.parseInt(left) without catching potential NumberFormatException,
though the regex check may prevent this in practice.

Referred Code
boolean leftIsSimpleNum = left.matches("\\d+") && Integer.parseInt(left) == leftM.get().getNumber();
boolean rightIsSimpleNum = right.matches("\\d+") && Integer.parseInt(right) == rightM.get().getNumber();

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-free-for-open-source-projects
Copy link
Contributor

qodo-free-for-open-source-projects bot commented Dec 28, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Learned
best practice
Cache Optional values for efficiency
Suggestion Impact:The suggestion was directly implemented. The commit extracts leftM.get() and rightM.get() into local variables leftMonth and rightMonth at lines 23-24, and then uses these variables throughout the format prioritization checks (lines 30-31, 43-44). However, lines 58 and 64 still use leftM.get() and rightM.get() instead of the extracted variables, which is a deviation from the suggestion.

code diff:

+        Month leftMonth = leftM.get();
+        Month rightMonth = rightM.get();
 
-        // Check for JabRef Format (#jan#)
-        boolean leftIsJabRef = left.equals(leftM.get().getJabRefFormat());
-        boolean rightIsJabRef = right.equals(rightM.get().getJabRefFormat());
-
+        boolean leftIsJabRef = left.equals(leftMonth.getJabRefFormat());
+        boolean rightIsJabRef = right.equals(rightMonth.getJabRefFormat());
         if (leftIsJabRef && !rightIsJabRef) {
             return ComparisonResult.LEFT_BETTER;
         }
@@ -45,10 +40,8 @@
             return ComparisonResult.RIGHT_BETTER;
         }
 
-        // Check for Standard Short Name (jan, feb...)
-        boolean leftIsShort = left.equalsIgnoreCase(leftM.get().getShortName());
-        boolean rightIsShort = right.equalsIgnoreCase(rightM.get().getShortName());
-
+        boolean leftIsShort = left.equalsIgnoreCase(leftMonth.getShortName());
+        boolean rightIsShort = right.equalsIgnoreCase(rightMonth.getShortName());

Extract leftM.get() and rightM.get() to local variables at the start of format
prioritization to avoid repeated Optional.get() calls and improve code
readability.

jablib/src/main/java/org/jabref/logic/bibtex/comparator/plausibility/MonthPlausibilityComparator.java [38-61]

-boolean leftIsJabRef = left.equals(leftM.get().getJabRefFormat());
-boolean rightIsJabRef = right.equals(rightM.get().getJabRefFormat());
+Month leftMonth = leftM.get();
+Month rightMonth = rightM.get();
+
+boolean leftIsJabRef = left.equals(leftMonth.getJabRefFormat());
+boolean rightIsJabRef = right.equals(rightMonth.getJabRefFormat());
 ...
-boolean leftIsShort = left.equalsIgnoreCase(leftM.get().getShortName());
-boolean rightIsShort = right.equalsIgnoreCase(rightM.get().getShortName());
+boolean leftIsShort = left.equalsIgnoreCase(leftMonth.getShortName());
+boolean rightIsShort = right.equalsIgnoreCase(rightMonth.getShortName());
 ...
-boolean leftIsSimpleNum = left.matches("\\d+") && Integer.parseInt(left) == leftM.get().getNumber();
-boolean rightIsSimpleNum = right.matches("\\d+") && Integer.parseInt(right) == rightM.get().getNumber();
+boolean leftIsSimpleNum = left.matches("\\d+") && Integer.parseInt(left) == leftMonth.getNumber();
+boolean rightIsSimpleNum = right.matches("\\d+") && Integer.parseInt(right) == rightMonth.getNumber();

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Optimize by performing redundant parsing operations only once instead of multiple times for the same value

Low
General
Simplify number parsing with try-catch
Suggestion Impact:The suggestion was directly implemented in the commit. The code was changed from using regex matching (left.matches("\\d+")) combined with Integer.parseInt() to using a try-catch block that catches NumberFormatException, exactly as suggested.

code diff:

+        boolean leftIsSimpleNum;
+        try {
+            leftIsSimpleNum = Integer.parseInt(left) == leftM.get().getNumber();
+        } catch (NumberFormatException e) {
+            leftIsSimpleNum = false;
+        }
+        boolean rightIsSimpleNum;
+        try {
+            rightIsSimpleNum = Integer.parseInt(right) == rightM.get().getNumber();
+        } catch (NumberFormatException e) {
+            rightIsSimpleNum = false;
+        }

Replace the regex check and Integer.parseInt() with a try-catch
(NumberFormatException) block for parsing simple numbers.

jablib/src/main/java/org/jabref/logic/bibtex/comparator/plausibility/MonthPlausibilityComparator.java [59-61]

 // Check for Simple Number
-boolean leftIsSimpleNum = left.matches("\\d+") && Integer.parseInt(left) == leftM.get().getNumber();
-boolean rightIsSimpleNum = right.matches("\\d+") && Integer.parseInt(right) == rightM.get().getNumber();
+boolean leftIsSimpleNum;
+try {
+    leftIsSimpleNum = Integer.parseInt(left) == leftM.get().getNumber();
+} catch (NumberFormatException e) {
+    leftIsSimpleNum = false;
+}
+boolean rightIsSimpleNum;
+try {
+    rightIsSimpleNum = Integer.parseInt(right) == rightM.get().getNumber();
+} catch (NumberFormatException e) {
+    rightIsSimpleNum = false;
+}

[Suggestion processed]

Suggestion importance[1-10]: 5

__

Why: The suggestion offers a more idiomatic and slightly more efficient way to parse integers by using a try-catch block, which improves code style and robustness.

Low
  • Update

@github-actions github-actions bot added the status: changes-required Pull requests that are not yet complete label Dec 28, 2025
@github-actions github-actions bot added status: changes-required Pull requests that are not yet complete and removed status: changes-required Pull requests that are not yet complete labels Dec 28, 2025
@keerthansamudrala keerthansamudrala marked this pull request as draft December 28, 2025 14:33
@calixtus calixtus added dev: code-quality Issues related to code or architecture decisions component: fetcher labels Dec 31, 2025
@github-actions
Copy link
Contributor

The requested changes were not addressed for 14 days. Please follow-up in the next 7 days or your PR will be automatically closed. You can check the contributing guidelines for hints on the pull request process.

@github-actions github-actions bot added the status: stale Issues marked by a bot as "stale". All issues need to be investigated manually. label Jan 15, 2026
@jabref-machine
Copy link
Collaborator

Your code currently does not meet JabRef's code guidelines. IntelliJ auto format covers some cases. There seem to be issues with your code style and autoformat configuration. Please reformat your code (Ctrl+Alt+L) and commit, then push.

@keerthansamudrala
Copy link
Author

Hi,

I converted the comment to a Javadoc-style block and ran IntelliJ’s reformat (⌘⌥L) with standard options (optimize imports, rearrange code, code cleanup).

IntelliJ reports “No lines changed: content is already properly formatted”, and I consistently end up with the current layout of the Javadoc comment.

Could you please confirm whether this formatting is acceptable and aligns with JabRef’s preferred style, or if you would prefer a different comment structure (for example, a single-line Javadoc or a different placement)?

Thanks!
Screenshot 2026-01-21 at 8 28 16 PM

@keerthansamudrala keerthansamudrala marked this pull request as ready for review January 21, 2026 15:03
@qodo-free-for-open-source-projects
Copy link
Contributor

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-free-for-open-source-projects
Copy link
Contributor

qodo-free-for-open-source-projects bot commented Jan 21, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Simplify comparator logic using ranking

Refactor the MonthPlausibilityComparator by introducing a helper method to rank
month formats numerically. This simplifies the main comparison logic from a
series of if-statements to a comparison of ranks, improving readability.

Examples:

jablib/src/main/java/org/jabref/logic/bibtex/comparator/plausibility/MonthPlausibilityComparator.java [14-78]
    public ComparisonResult compare(String left, String right) {
        Optional<Month> leftM = Month.parse(left);
        Optional<Month> rightM = Month.parse(right);

        if (leftM.isPresent() && rightM.isEmpty()) {
            return ComparisonResult.LEFT_BETTER;
        }
        if (rightM.isPresent() && leftM.isEmpty()) {
            return ComparisonResult.RIGHT_BETTER;
        }

 ... (clipped 55 lines)

Solution Walkthrough:

Before:

public ComparisonResult compare(String left, String right) {
    // ... initial checks for valid months ...

    boolean leftIsJabRef = left.equals(leftMonth.getJabRefFormat());
    boolean rightIsJabRef = right.equals(rightMonth.getJabRefFormat());
    if (leftIsJabRef && !rightIsJabRef) { return ComparisonResult.LEFT_BETTER; }
    if (rightIsJabRef && !leftIsJabRef) { return ComparisonResult.RIGHT_BETTER; }

    boolean leftIsShort = left.equalsIgnoreCase(leftMonth.getShortName());
    boolean rightIsShort = right.equalsIgnoreCase(rightMonth.getShortName());
    if (leftIsShort && !rightIsShort) { return ComparisonResult.LEFT_BETTER; }
    if (rightIsShort && !leftIsShort) { return ComparisonResult.RIGHT_BETTER; }

    // ... similar checks for number format and string length ...

    return ComparisonResult.UNDETERMINED;
}

After:

private int getRank(String value, Month month) {
    if (value.equals(month.getJabRefFormat())) return 0; // JabRef format
    if (value.equalsIgnoreCase(month.getShortName())) return 1; // Short name
    try {
        if (Integer.parseInt(value) == month.getNumber()) return 2; // Number
    } catch (NumberFormatException e) {
        // Not a number
    }
    return 3; // Other (e.g., full name)
}

public ComparisonResult compare(String left, String right) {
    // ... initial checks for valid months ...
    int leftRank = getRank(left, leftMonth);
    int rightRank = getRank(right, rightMonth);

    if (leftRank < rightRank) return ComparisonResult.LEFT_BETTER;
    if (rightRank < leftRank) return ComparisonResult.RIGHT_BETTER;

    // ... handle tie-breaking, e.g., by length ...
    return ComparisonResult.UNDETERMINED;
}
Suggestion importance[1-10]: 7

__

Why: The suggestion provides a valid and significant improvement to the code's design by replacing a long series of conditional checks in MonthPlausibilityComparator with a more readable and maintainable ranking system.

Medium
General
Prefer canonical month name capitalization

Improve the comparison logic by adding a check to prefer the canonical
capitalization for full month names (e.g., "June" over "june") before falling
back to string length comparison.

jablib/src/main/java/org/jabref/logic/bibtex/comparator/plausibility/MonthPlausibilityComparator.java [70-77]

+if (left.equalsIgnoreCase(right)) {
+    // Prefer canonical capitalization
+    if (left.equals(month.getFullName())) {
+        return ComparisonResult.LEFT_BETTER;
+    }
+    if (right.equals(month.getFullName())) {
+        return ComparisonResult.RIGHT_BETTER;
+    }
+}
+
 if (left.length() < right.length()) {
     return ComparisonResult.LEFT_BETTER;
 }
 if (right.length() < left.length()) {
     return ComparisonResult.RIGHT_BETTER;
 }
 
 return ComparisonResult.UNDETERMINED;
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion proposes a valuable improvement to the comparison logic by adding a check for canonical capitalization (e.g., "June" over "june"), which makes the comparator more robust and deterministic for full month names.

Low
Extract duplicated logic to a method

Extract the duplicated logic for checking if a string is a numeric month
representation into a new private helper method to reduce code duplication.

jablib/src/main/java/org/jabref/logic/bibtex/comparator/plausibility/MonthPlausibilityComparator.java [52-63]

-boolean leftIsSimpleNum;
-try {
-    leftIsSimpleNum = Integer.parseInt(left) == leftM.get().getNumber();
-} catch (NumberFormatException e) {
-    leftIsSimpleNum = false;
-}
-boolean rightIsSimpleNum;
-try {
-    rightIsSimpleNum = Integer.parseInt(right) == rightM.get().getNumber();
-} catch (NumberFormatException e) {
-    rightIsSimpleNum = false;
-}
+boolean leftIsSimpleNum = isSimpleNumber(left, month);
+boolean rightIsSimpleNum = isSimpleNumber(right, month);
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: This suggestion correctly points out duplicated logic and proposes extracting it into a helper method, which is a good practice that improves code readability and maintainability by adhering to the DRY principle.

Low
Avoid redundant variable declarations

Replace the redundant leftMonth and rightMonth variables with a single month
variable, as they are guaranteed to be equal.

jablib/src/main/java/org/jabref/logic/bibtex/comparator/plausibility/MonthPlausibilityComparator.java [27-32]

 if (!leftM.equals(rightM)) {
     return ComparisonResult.UNDETERMINED;
 }
 
-Month leftMonth = leftM.get();
-Month rightMonth = rightM.get();
+// Both leftM and rightM are present and contain the same month.
+Month month = leftM.get();
  • Apply / Chat
Suggestion importance[1-10]: 4

__

Why: The suggestion correctly identifies that leftMonth and rightMonth will hold the same value and can be replaced by a single variable, which improves code clarity and reduces redundancy.

Low
Learned
best practice
Fix duplicate word in comment
Suggestion Impact:The commit fixed the duplicate word and reformatted the comment from multi-line to single-line format. However, it changed the comment style from JavaDoc (///) to regular comment (//) instead of keeping the JavaDoc format.

code diff:

-/// If
-/// the
-/// left
-/// entry
-/// type
-/// is
-/// misc
-/// then
-/// prefer
-/// the
-/// the
-/// right
-/// value
+// If the left entry type is misc then prefer the right value

The JavaDoc comment contains a duplicate word "the the" and has been incorrectly
reformatted into a multi-line format with one word per line. Fix the typo and
restore proper single-line formatting.

jablib/src/main/java/org/jabref/logic/bibtex/comparator/plausibility/EntryTypePlausibilityComparator.java [6-18]

-/// If
-/// the
-/// left
-/// entry
-/// type
-/// is
-/// misc
-/// then
-/// prefer
-/// the
-/// the
-/// right
-/// value
+/// If the left entry type is misc then prefer the right value

[Suggestion processed]

Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Correct typographical errors in documentation and code comments to maintain code quality and professionalism.

Low
  • More


/// If the left entry type is misc then prefer the the right value
/// If
/// the
Copy link
Member

Choose a reason for hiding this comment

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

rever the javadoc formatting here

@github-actions
Copy link
Contributor

Your pull request conflicts with the target branch.

Please merge with your code. For a step-by-step guide to resolve merge conflicts, see https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-using-the-command-line.

@koppor
Copy link
Member

koppor commented Jan 24, 2026

Could you please confirm whether this formatting is acceptable and aligns with JabRef’s preferred style, o

You can research why IntelliJ does that line breaking... And search for IntelliJ issues... We saw this behavior but did not track it down to the root cause yet.

Copy link
Member

@koppor koppor left a comment

Choose a reason for hiding this comment

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

SPAM!!

This copies #14629 and claims to be the original author.

@koppor koppor closed this Jan 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: fetcher dev: code-quality Issues related to code or architecture decisions first contrib good first issue An issue intended for project-newcomers. Varies in difficulty. Review effort 2/5 status: changes-required Pull requests that are not yet complete status: stale Issues marked by a bot as "stale". All issues need to be investigated manually.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add MonthPlausibilityComparator

5 participants