Skip to content
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We fixed an issue where Document Viewer showed technical exceptions when opening entries with non-PDF files. [#13198](https://github.com/JabRef/jabref/issues/13198)
- When creating a library, if you drag a PDF file containing only a single column, the dialog will now automatically close. [#13262](https://github.com/JabRef/jabref/issues/13262)
- We fixed an issue where the tab showing the fulltext search results would appear blank after switching library. [#13241](https://github.com/JabRef/jabref/issues/13241)
- Enhanced field selection logic in the Merge Entries dialog when fetching from DOI to prefer valid years and entry types. [#12549](https://github.com/JabRef/jabref/issues/12549)

### Removed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ private void showMergeDialog(BibEntry originalEntry, BibEntry fetchedEntry, WebF
dialog.setTitle(Localization.lang("Merge entry with %0 information", fetcher.getName()));
dialog.setLeftHeaderText(Localization.lang("Original entry"));
dialog.setRightHeaderText(Localization.lang("Entry from %0", fetcher.getName()));
dialog.autoSelectBetterFields();
Optional<BibEntry> mergedEntry = dialogService.showCustomDialogAndWait(dialog).map(EntriesMergeResult::mergedEntry);

if (mergedEntry.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,8 @@ public void setRightHeaderText(String rightHeaderText) {
public void configureDiff(ShowDiffConfig diffConfig) {
threeWayMergeView.showDiff(diffConfig);
}

public void autoSelectBetterFields() {
threeWayMergeView.autoSelectBetterFields();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ public boolean hasEqualLeftAndRightValues() {
return viewModel.hasEqualLeftAndRightValues();
}

public void autoSelectBetterValue() {
viewModel.autoSelectBetterValue();
}

@Override
public String toString() {
return "FieldRowView [shouldShowDiffs=" + shouldShowDiffs.get() + ", fieldNameCell=" + fieldNameCell + ", leftValueCell=" + leftValueCell + ", rightValueCell=" + rightValueCell + ", mergedValueCell=" + mergedValueCell + "]";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@

import org.jabref.gui.mergeentries.newmergedialog.fieldsmerger.FieldMerger;
import org.jabref.gui.mergeentries.newmergedialog.fieldsmerger.FieldMergerFactory;
import org.jabref.logic.bibtex.comparator.ComparisonResult;
import org.jabref.logic.bibtex.comparator.YearFieldValuePlausibilityComparator;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.InternalField;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.entry.types.EntryTypeFactory;
import org.jabref.model.entry.types.StandardEntryType;
import org.jabref.model.strings.StringUtil;

import com.tobiasdiez.easybind.EasyBind;
Expand Down Expand Up @@ -120,6 +124,25 @@ public FieldRowViewModel(Field field, BibEntry leftEntry, BibEntry rightEntry, B
EasyBind.subscribe(hasEqualLeftAndRightBinding(), this::setIsFieldsMerged);
}

public void autoSelectBetterValue() {
String leftValue = getLeftFieldValue();
String rightValue = getRightFieldValue();

if (StandardField.YEAR == field) {
YearFieldValuePlausibilityComparator comparator = new YearFieldValuePlausibilityComparator();
ComparisonResult comparison = comparator.compare(leftValue, rightValue);
if (ComparisonResult.RIGHT_BETTER == comparison) {
selectRightValue();
} else if (ComparisonResult.LEFT_BETTER == comparison) {
selectLeftValue();
}
} else if (InternalField.TYPE_HEADER == field) {
if (leftValue.equalsIgnoreCase(StandardEntryType.Misc.getName())) {
selectRightValue();
}
}
}

public void selectNonEmptyValue() {
if (StringUtil.isNullOrEmpty(leftFieldValue.get())) {
selectRightValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,10 @@ public BibEntry getRightEntry() {
public void saveConfiguration() {
toolbar.saveToolbarConfiguration();
}

public void autoSelectBetterFields() {
for (FieldRowView row : fieldRows) {
row.autoSelectBetterValue();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,24 @@ void unmergeFieldsShouldDoNothingIfFieldsAreNotMerged() {
assertEquals(oldRightGroups, groupsField.getRightFieldValue());
}

@Test
void newYearShouldBeSelectedForYearsWithLargeValueGap() {
BibEntry leftEntry = new BibEntry().withField(StandardField.YEAR, "1990");
BibEntry rightEntry = new BibEntry().withField(StandardField.YEAR, "2020");
FieldRowViewModel yearField = new FieldRowViewModel(StandardField.YEAR, leftEntry, rightEntry, mergedEntry, fieldMergerFactory);
yearField.autoSelectBetterValue();
assertEquals(FieldRowViewModel.Selection.RIGHT, yearField.getSelection());
}

@Test
void yearInRangeShouldBeSelected() {
BibEntry leftEntry = new BibEntry().withField(StandardField.YEAR, "1700");
BibEntry rightEntry = new BibEntry().withField(StandardField.YEAR, "2000");
FieldRowViewModel yearField = new FieldRowViewModel(StandardField.YEAR, leftEntry, rightEntry, mergedEntry, fieldMergerFactory);
yearField.autoSelectBetterValue();
assertEquals(FieldRowViewModel.Selection.RIGHT, yearField.getSelection());
}

public FieldRowViewModel createViewModelForField(Field field) {
return new FieldRowViewModel(field, leftEntry, rightEntry, mergedEntry, fieldMergerFactory);
}
Expand Down
1 change: 1 addition & 0 deletions jablib/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -249,5 +249,6 @@
requires mslinks;
requires org.antlr.antlr4.runtime;
requires org.libreoffice.uno;
requires org.jetbrains.annotations;
// endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.jabref.logic.bibtex.comparator;

public enum ComparisonResult {
LEFT_BETTER,
RIGHT_BETTER,
UNDETERMINED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.jabref.logic.bibtex.comparator;

public abstract class FieldValuePlausibilityComparator {
/**
* Compares the plausibility of two field values.
*
* @param leftValue value from the library (or candidate)
* @param rightValue value from the fetcher (or existing record)
* @return ComparisonResult indicating which field is more plausible: RIGHT_BETTER, LEFT_BETTER, or UNDETERMINED
*/
public abstract ComparisonResult compare(String leftValue, String rightValue);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.jabref.logic.bibtex.comparator;

import java.time.Year;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jabref.logic.integrity.YearChecker;

public class YearFieldValuePlausibilityComparator extends FieldValuePlausibilityComparator {

private static final Pattern YEAR_PATTERN = Pattern.compile("(\\d{4})");

/**
* Compares the plausibility of two field values.
*
* @param leftValue value from the library (or candidate)
* @param rightValue value from the fetcher (or existing record)
* @return ComparisonResult indicating which year is more plausible: RIGHT_BETTER, LEFT_BETTER, or UNDETERMINED
*/

@Override
public ComparisonResult compare(String leftValue, String rightValue) {
YearChecker checker = new YearChecker();

boolean leftValid = checker.checkValue(leftValue).isEmpty();

if (leftValid) {
Optional<Integer> leftYear = extractYear(leftValue);
Optional<Integer> rightYear = extractYear(rightValue);

boolean leftYearInRange = (leftYear.get() >= 1800) && (leftYear.get() <= Year.now().getValue() + 2);

if (leftYearInRange) {
int diff = Math.abs(leftYear.get() - rightYear.get());
if (diff > 10) {
return rightYear.get() > leftYear.get()
? ComparisonResult.RIGHT_BETTER
: ComparisonResult.LEFT_BETTER;
}
return ComparisonResult.UNDETERMINED; // years are close, undetermined
}
return ComparisonResult.RIGHT_BETTER;
}
return ComparisonResult.RIGHT_BETTER;
}

/**
* Extracts the first 4-digit number found in the string.
* Used to identify year-like values such as "About 2000" or "Published in 1999".
*
* @param value the input string possibly containing a year
* @return Optional containing the 4-digit year if found, otherwise Optional.empty()
*/
private Optional<Integer> extractYear(String value) {
Matcher matcher = YEAR_PATTERN.matcher(value);
if (matcher.find()) {
return Optional.of(Integer.parseInt(matcher.group(1)));
}
return Optional.empty();
}
}